<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7108966</id><updated>2011-04-22T01:57:55.403Z</updated><title type='text'>Living Code</title><subtitle type='html'>A program is a process, not a thing.  This also applies to life, the universe, and everything.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>45</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7108966.post-114663549898127088</id><published>2006-05-03T05:16:00.000Z</published><updated>2007-02-02T03:04:40.986Z</updated><title type='text'>Last Post</title><content type='html'>This will be my last post on Blogger, but fear not!  I have imported all my Blogger articles (and I'll add my older ManilaSites articles eventually) on my new site:  &lt;a href="http://livingcode.org/"&gt;livingcode.org&lt;/a&gt;.  Um, yeah, it's still called Living Code, but now it's on the livingcode site, which makes all the difference.  It's driven by my new Pythonalicious blogging tool: Sandcastle.&lt;br /&gt;&lt;br /&gt;What didn't make it across are the comments on blogger.  While I've had a few really good comments here, the ratio of spam to comments has been completely out of whack.  I'm just going to put up my email address and let the spam filters take care of it.  Hopefully real comments will be able to get through.&lt;br /&gt;&lt;br /&gt;It's taken longer than I'd hoped to make this transition, but now I hope to be able to concentrate on posting some of the examples I've written for PyObjC  and writing some new tutorials.  I'm also going to get the projects I'm discussing into publicly accessible subversion repositories Real Soon Now&amp;trade;&lt;br /&gt;&lt;br /&gt;The new blog is fully tag-enabled.  There is a main Atom feed, and each tag has its own Atom feed, so for instance if you're only interested in Python articles, you can just subscribe to the Python tag-feed and you won't have to hear about my Make Magazine-inspired projects with the kids.  Now I just need to add some Ajax and it will be fully buzzword-compliant.&lt;br /&gt;&lt;br /&gt;Hope to see y'all there!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-114663549898127088?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/114663549898127088/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=114663549898127088' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/114663549898127088'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/114663549898127088'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2006/05/last-post.html' title='Last Post'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-114197491022369614</id><published>2006-03-10T07:04:00.000Z</published><updated>2006-05-03T00:13:49.480Z</updated><title type='text'>Screenshot in Cocoa (Python)</title><content type='html'>I noticed that jwz is trying to &lt;a href="http://jwz.livejournal.com/608233.html"&gt;take a screenshot&lt;/a&gt; in Cocoa and having trouble. I'm happy to see that he's porting his awesome collection of screensavers to OS X, that's great news.  As for taking screen shots, I'm amazed it is as hard as it seems to be--heck even the new Nokia Series 60 give you the ability to take screeenshots from Python now.  In any case, here is a method that works for me under PyObjC.  It assumes it is part of a Cocoa object and that you've done a &lt;code&gt;from AppKit import *&lt;/code&gt; already.  Since I can't comment on jwz's blog (whether it's because I don't use LiveJournal or because I do use Safari, I don't know), I'll post it here instead.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;def screenShot(self):&lt;br /&gt;    rect = NSScreen.mainScreen().frame()&lt;br /&gt;    image = NSImage.alloc().initWithSize_((rect.size.width, rect.size.height&lt;br /&gt;))&lt;br /&gt;    window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(&lt;br /&gt;            rect, &lt;br /&gt;            NSBorderlessWindowMask, &lt;br /&gt;            NSBackingStoreNonretained, &lt;br /&gt;            False)&lt;br /&gt;    view = NSView.alloc().initWithFrame_(rect)&lt;br /&gt;    window.setLevel_(NSScreenSaverWindowLevel + 100)&lt;br /&gt;    window.setHasShadow_(False)&lt;br /&gt;    window.setAlphaValue_(0.0)&lt;br /&gt;    window.setContentView_(view)&lt;br /&gt;    window.orderFront_(self)&lt;br /&gt;    view.lockFocus()&lt;br /&gt;    screenRep= NSBitmapImageRep.alloc().initWithFocusedViewRect_(rect)&lt;br /&gt;    image.addRepresentation_(screenRep)&lt;br /&gt;    view.unlockFocus()&lt;br /&gt;    window.orderOut_(self)&lt;br /&gt;    window.close()&lt;br /&gt;    return image&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I cribbed this several months ago from some example Cocoa code, but forgot to make a note of where I got it.  If anyone recognizes this pattern, please let me know so I can attribute it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-114197491022369614?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/114197491022369614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=114197491022369614' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/114197491022369614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/114197491022369614'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2006/03/screenshot-in-cocoa-python.html' title='Screenshot in Cocoa (Python)'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-114137029949277776</id><published>2006-03-03T07:00:00.000Z</published><updated>2006-03-03T07:27:29.860Z</updated><title type='text'>Silent Blog</title><content type='html'>Yes, I've been quiet for awhile.  No, I'm not going to apologize, and I wish more bloggers would stop apologizing when they take time off.  One of the great things about Atom/RSS feeds is that I can keep up with blogs that are posted to infrequently, which includes some of my favorites.  Don't feel like you need to post all the time--we've all got plenty of other things to read!&lt;br /&gt;&lt;br /&gt;My quiet time is going to continue for a bit longer, but while I won't apologize, I will at least explain.  Here's what I'm doing instead with the couple of hours I have between putting the kids to bed and going to bed myself.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Writing the OS X version of the file uploader for &lt;a href="http://www.conversationsnetwork.org/"&gt;The Conversation Network&lt;/a&gt;.  At some point this will be made public, and the idea is to open source it.  I'll be sure to point that out when it happens.  It's been really great to have this opportunity to work with Doug Kaye and the rest of the gang from ITConversations, after being an admirer (and consumer) of their work for some time.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Attended the &lt;a href="http://www.northernvoice.ca/"&gt;Northern Voice&lt;/a&gt; and &lt;a href="http://2006.northernvoice.ca/moosecamp"&gt;Moose Camp&lt;/a&gt; conference, where I was fortunate enough to co-moderate a session on community and blogs with &lt;a href="http://www.fullcirc.com/weblog/onfacblog.htm"&gt;Nancy White&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Writing my own weblog software. Blogger has a better interface than manilasites, but that's not saying much.  I'm also trying to port my old content from manilasites, plus my pre-blog-era work, plus my paper journal.  All of this will be hosted at livingcode.org in the near future.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Writing more articles for IBM developerWorks, I hope to have more to say about that Real Soon Now&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Generally organizing the livingcode site better, getting projects their own pages, putting code into SVN.  Trying to use Sourceforge was a huge failure, their interface is just too crufty and they still don't support Subversion.  But Dreamhost (where livingcode.org is hosted) does support Subversion now, so I'm going to be moving all my public code over there.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;I wrote a screensaver using PyObjC which I'm hoping to get included in their distribution as an example, but I need to do some refactoring first (in the time leftover from other projects).  I want to write that up too, it was pretty interesting to work on.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;I periodically take a stab at porting VPython to the Aqua environment.  I have some ideas for this (some of them probably heretical to the VPython folks), but need time to work on them.  Notice a pattern emerging?&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Porting Apple's Sketch demo application from ObjectiveC to PyObjC.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Several of these things are just precursors to DrawingBoard, my animation program for kids.  I really, really want to be working on that, but need to get some of the smaller projects finished and out of my head so I can concentrate.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Exploring programming environments for my kids. My nine-year-old daughter and I will try competing in the &lt;a href="http://www.pyweek.org/"&gt;PyWeek&lt;/a&gt; game challenge later this month.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;I'm sure I've forgotten several significant things.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;More news as it happens.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-114137029949277776?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/114137029949277776/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=114137029949277776' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/114137029949277776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/114137029949277776'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2006/03/silent-blog.html' title='Silent Blog'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-113610606015670530</id><published>2006-01-01T08:32:00.000Z</published><updated>2006-01-01T09:33:38.853Z</updated><title type='text'>Reflecting on the past, looking to the future</title><content type='html'>Programming is the art of telling stories to the computer in such a way that the computer can tell us interesting stories back. At its best, these stories help us to write even better stories, and to share them with each other.&lt;br /&gt;&lt;br /&gt;I see computers as not ready yet.  People buy them, they're interesting toys.  They help in some ways, make things more difficult in others.  But we have bought into the mythology of computers.  Meanwhile, the state of the art in computing has moved backwards in important ways[1], even though computers are getting cheaper and faster.  Innovation crawl during the dotcom boom[2]. Tools get more complicated[3], resulting in less freedom of expression, wasting the time of the people who use them.&lt;br /&gt;&lt;br /&gt;The idea behind Living Code is that software is not a thing that is finished, it is more like a conversation.  Like telling a story that someone will make better.  We are trying to take steps to move to a new model of software, one in which we don't "write programs," but instead play the computer like a musician plays jazz. This model should be accessible to children, and yes, even to adults.&lt;br /&gt;&lt;br /&gt;Empowering.&lt;br /&gt;&lt;br /&gt;A rather large goal.&lt;br /&gt;&lt;br /&gt;But just as software is never finished, the goal isn't there to be achieved.  We have a goal in order to make focused, achievable steps while learning from history and aware of our limitations.  In this conversation our goals become prototypes, experiments which are contantly evolving as they are tested in use.&lt;br /&gt;&lt;br /&gt;Happy New Year!&lt;br /&gt;&lt;br /&gt;[1] For examples of the promise that computers have thus far failed to live up to, see the wikipedia articles for &lt;a href="http://en.wikipedia.org/wiki/Sketchpad"&gt;Sketchpad&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Dynabook"&gt;Dynabook&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/OpenDoc"&gt;OpenDoc&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Seymour_Papert"&gt;Seymour Papert&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/Douglas_Engelbart"&gt;Douglas Englebart&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;[2] For examples of how computer innovation has stalled, check out one of many &lt;a href="http://appft1.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&amp;Sect2=HITOFF&amp;d=PG01&amp;p=1&amp;u=%2Fnetahtml%2FPTO%2Fsrchnum.html&amp;r=1&amp;f=G&amp;l=50&amp;s1=%2220050246635%22.PGNR.&amp;OS=DN/20050246635&amp;RS=DN/20050246635"&gt;patents&lt;/a&gt; holding the field back, or check out this &lt;a href="http://www.nimh.org/microsoft/"&gt;summary&lt;/a&gt; of Microsoft's innovations. &lt;br /&gt;&lt;br /&gt;[3] For an example of how tools are getting too complicated, see Charles Petzold's article, &lt;a href="http://www.charlespetzold.com/etc/DoesVisualStudioRotTheMind.html"&gt;Does Visual Studio Rot the Mind?&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-113610606015670530?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/113610606015670530/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=113610606015670530' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113610606015670530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113610606015670530'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2006/01/reflecting-on-past-looking-to-future.html' title='Reflecting on the past, looking to the future'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-113484501788688845</id><published>2005-12-17T18:20:00.000Z</published><updated>2006-03-19T22:35:03.956Z</updated><title type='text'>Sierra Sliders</title><content type='html'>Kathy Sierra, of the &lt;a href="http://headrush.typepad.com/creating_passionate_users/2005/11/how_to_come_up_.html"&gt;Creating Passionate Users&lt;/a&gt; blog, posted about using an equalizer metaphor for product planning and brainstorming. She included some images for her readers to play around with for their own equalizer-planning projects.  I took those, apply the Gimp to them, mixed with Bob Ippolito's uber-cool &lt;a href="http://mochikit.org/"&gt;MochiKit&lt;/a&gt;, and release to you:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://livingcode.org/miniproject/sliders/"&gt;&lt;p&gt;Sierra's Sliders&lt;/p&gt;&lt;br /&gt;&lt;img border="0" width="339" height="269" src="http://photos1.blogger.com/blogger/5552/17/320/equalizer_example.jpg" alt="Equalizer Example" /&gt;&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;You can label up to eight sliders using the text box, hit enter and tweak the sliders.  When you have it the way you want it, you can bookmark the result and send it to your team members, or your mom.  Tested in IE 6, Firefox 1.5, and Safari 2.0.2.  Your mileage may vary. Void where prohibited by law. Some limitations may apply. Coded in a hurry&lt;sup&gt;&amp;trade;&lt;/sup&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-113484501788688845?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/113484501788688845/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=113484501788688845' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113484501788688845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113484501788688845'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/12/sierra-sliders.html' title='Sierra Sliders'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-113471322007417516</id><published>2005-12-16T04:42:00.000Z</published><updated>2005-12-16T06:08:52.330Z</updated><title type='text'>3D, it's not just for breakfast anymore</title><content type='html'>I've been fooling around with 3D lately.  First off, my third article as guest-writer for &lt;a href="http://www.gnosis.cx/publish/"&gt;David Mertz's&lt;/a&gt; XML Matters column, &lt;a href="http://www-128.ibm.com/developerworks/web/library/x-matters43/index.html"&gt;The Web ain't just for 2D anymore&lt;/a&gt; went live on IBM's developerWorks site today.  It's about X3D (3D in XML), successor to VRML, and the possibility of it being relevant today.  I have moderate hope for it, now that SVG is starting to be a player.  The funny thing is, I think X3D is probably less complicated to implement than SVG is.  The real coolness starts when you can combine them, but that is still a ways off.&lt;br /&gt;&lt;br /&gt;Years ago, I was the lead programmer for &lt;a href="http://antarcti.ca/"&gt;Antarcti.ca's&lt;/a&gt; 3D web client (which was discontinued awhile back), and before that I implemented a simple 3D renderer in Java AWT (this was before Swing, and &lt;em&gt;way&lt;/em&gt; before Java3D.  So I've been tinkering around with 3D for awhile.  Lately I've volunteered to take a stab at porting &lt;a href="http://vpython.org"&gt;VPython&lt;/a&gt; over to OS X Aqua (it can be built for OS X, but only under X Windows, which doesn't appeal to me).  So far, the build process for it has been stumping me, and soaking up what little time I have to devote to my hobby coding, but I still plug away at it from time to time. It's a C++ extension for Python which relies on boost, glib, and OpenGL libraries, and it uses autoconf in a fairly non-auto way.  I've never been expert at build systems, most python projects I've needed were either &lt;code&gt;.configure;make;make install&lt;/code&gt; or &lt;code&gt;python setup.py install&lt;/code&gt;, so the struggle to port this really bugs me, but VPython is a very cool project and I want to use it (and I don't want to give in and rely on fink and X).  Sometimes I'm too stubborn for my own good.&lt;br /&gt;&lt;br /&gt;In a previous post I mentioned that I was thinking of writing a tool for screencasting from OS X.  It turns out that while Apple has included more advanced Cocoa libraries for Quicktime in Tiger, there isn't a convenient way to create new, writable movies from Cocoa, so that project has stalled, for the time being.  There is a solution, but I'm trying to wrap up other things before I delve into it.&lt;br /&gt;&lt;br /&gt;I have two projects nearly ready to release which are both larger examples of using PyObjC.  One is my own project, DrawingBoard, which is being tested right now by both my kids and my friend &lt;a href="http://fergusson.net/"&gt;Michael's&lt;/a&gt; kids.  I'm about ready to let other people see it, rough as it still is.  The other project is a port of Apple's Sketch example code from Objective-C into Python, which gives examples of how to use Core Data, undo/redo, and many other things.  Both will be coming soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-113471322007417516?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/113471322007417516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=113471322007417516' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113471322007417516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113471322007417516'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/12/3d-its-not-just-for-breakfast-anymore.html' title='3D, it&apos;s not just for breakfast anymore'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-113097874930004658</id><published>2005-11-03T00:29:00.000Z</published><updated>2006-02-11T00:19:16.963Z</updated><title type='text'>Interface Builder vs. Macromedia Flex Builder 2</title><content type='html'>I recently tried out the beta of Macromedia Flex Builder 2, and was quite impressed. I normally avoid Flash on principle, but it has some pretty powerful tools built in.  It feels more limited than Apple's Interface Builder, but it has one feature that I've been dying to see in IB: You can flip between visual drag-and-drop widget mode, and editing the layout as XML.  Interface Builder so needs this ability.  It would help for folks writing about developing for OS X (sometimes 100 words is better than half a dozen pictures, and trying to show CTRL-dragging in a still picture is an exercise in futility), and it would help when you come to a new project (or one you haven't worked on in awhile) and want to get a feel for what methods and event handlers are hooked in to various widgets.  Heck, it would help with automated tools, with testing, with grep.  Just do it, Apple, or hire me to do it.&lt;br /&gt;&lt;br /&gt;The other part that was interesting for me was that Flex Builder runs inside of Eclipse.  It's been a long time since I've tried Eclipse and I was pleasantly suprised.  It was fairly snappy, not too confusing to find my way around in, and looked better than I remembered.  Of course, I was running it on a dual 3GHz Windows box, so I might be disappointed once more if I ran it on my Powerbook, but my brief encounter with it didn't suck, which was a big improvement.&lt;br /&gt;&lt;br /&gt;Of course, neither of these developments are going to lure me away from Python and Vim any time soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-113097874930004658?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/113097874930004658/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=113097874930004658' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113097874930004658'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113097874930004658'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/11/interface-builder-vs-macromedia-flex.html' title='Interface Builder vs. Macromedia Flex Builder 2'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-113038652550168466</id><published>2005-10-27T03:57:00.000Z</published><updated>2006-04-27T07:06:33.090Z</updated><title type='text'>Declarative animation with SVG</title><content type='html'>One of the things which has been taking up my time from releasing Drawing Board is that I've been writing another guest edition of my friend David Mertz's &lt;a href="http://www-128.ibm.com/developerworks/views/xml/libraryview.jsp?search_by=xml+matters:"&gt;XML Matters&lt;/a&gt; column for IBM developerWorks.  The latest, &lt;a href="http://www-128.ibm.com/developerworks/xml/library/x-matters42/"&gt;SVG and the Scriptless Script&lt;/a&gt; went live yesterday.  &lt;br /&gt;&lt;br /&gt;My last article, on using the DOM, was something I know well and have used for years.  This one was on a subject I've wanted to learn more about, so getting the examples all working the way I wanted was challenging, but I'm really happy with the result. &lt;br /&gt;&lt;br /&gt;&amp;lt;teaser&amp;gt;Another reason I haven't released Drawing Board yet is that I want to put up a screencast of it in use, but there doesn't appear to be a good solution for creating screencasts on OS X.  I think I've got a solution for that.&amp;lt/teaser&amp;gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-113038652550168466?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/113038652550168466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=113038652550168466' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113038652550168466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/113038652550168466'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/10/declarative-animation-with-svg.html' title='Declarative animation with SVG'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-112969861135272161</id><published>2005-10-19T04:25:00.000Z</published><updated>2006-03-14T18:44:53.473Z</updated><title type='text'>PySight Preview</title><content type='html'>Awhile back I promised a bunch of posts, but delays were made (including a month of vacation travelling around BC which I won't apologize for).  One of the promised projects I was going to talk about was PySight, which ought to be simple, since it's just a trivial wrapper around Tim Omernick's &lt;a href="http://www.skyfell.org/cocoasequencegrabber.html"&gt;CocoaSequenceGrabber&lt;/a&gt; (used with his permission). But I wanted to package it nicely, write more example code, maybe some documentation.&lt;br /&gt;&lt;br /&gt;So instead of a polished project I have no project, and finishing it is pretty low on my priorities right now, sad to say.&lt;br /&gt;&lt;br /&gt;Fortunately, Robbie Tingey came to the rescue and prompted me about it.  I put a zip file together with Tim's code to create a framework, his example program to use the framework, my simple wrapper, and my re-write of Tim's example program in python using PyObjC to show how to use this.  There's a README, but not much else.  I sent Robbie the URL and he tried it out successfully, so I thought I'd toss it out to the rest of the world.  Caveat emptor, this is pre-alpha, no guarantees, no promises, but hey, it "Works for me&amp;trade;."&lt;br /&gt;&lt;br /&gt;So if you're feeling adventurous, go ahead and try out &lt;a href="http://livingcode.org/pysight/PySight.zip"&gt;PySight&lt;/a&gt; (74K Zip) and start grabbing data from your iSight camera from Python.  Contributions to packaging it nicely, documenting it, or adding examples are gratefully accepted.  Or, just bug me about it and I'll see what I can do to move it up my priority list.&lt;br /&gt;&lt;br /&gt;Python, meet iSight.  iSight, meet Python.  Play nice together now.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Update: I forgot the link.  Thanks, Marcia!&lt;/strong&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-112969861135272161?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/112969861135272161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=112969861135272161' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/112969861135272161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/112969861135272161'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/10/pysight-preview.html' title='PySight Preview'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-112144349561902038</id><published>2005-07-15T15:56:00.000Z</published><updated>2006-08-02T08:49:36.770Z</updated><title type='text'>Sex, guns, and Clinton</title><content type='html'>I'm kind of shocked about the hidden sex scenes in Grand Theft Auto. I mean, I'm shocked that so many are shocked about it. I've never played it, but my understanding based on seeing reviews is that this is a game in which you are rewarded for stealing cars, hitting pedestrians, and killing cops.  So adding sexually explicit material is what gets it pulled from WallMart?&lt;br /&gt;&lt;br /&gt;Dude, where's your society's sense of perspective?  Where are our priorities?&lt;br /&gt;&lt;br /&gt;Why can TV show a kid thousands of murders and simultaneously pretend that the human body is somehow dirty?  Frankly, I'd be less upset if my kids accidently saw Debbie Does Dallas than if they saw any of the gore and gun porn that passes for Hollywood movies these days.&lt;br /&gt;&lt;br /&gt;And is it just me, or when you see a BBC headline like &lt;a href="http://news.bbc.co.uk/1/hi/technology/4682533.stm"&gt;Clinton wades into GTA sex storm&lt;/a&gt; you immediately think of the &lt;em&gt;other&lt;/em&gt; Clinton?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-112144349561902038?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/112144349561902038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=112144349561902038' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/112144349561902038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/112144349561902038'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/07/sex-guns-and-clinton.html' title='Sex, guns, and Clinton'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-112123211602654925</id><published>2005-07-13T04:55:00.000Z</published><updated>2005-07-13T05:22:51.700Z</updated><title type='text'>Born-Again Javascript</title><content type='html'>While most of my coding for fun is in Python, a whole lot of my coding at work is in Javascript.  Over the years I have tried &lt;a href="http://dynapi.sourceforge.net/"&gt;many&lt;/a&gt;, &lt;a href="http://www.technicalpursuit.com/"&gt;many&lt;/a&gt; frameworks for extending Javascript to add new widgets to the browser, to handle functional-style programming in Javascript, or simply to make the browser DOM easier to use.  They pretty much all suck.  Much of the suckitude comes from trying to support beasts such as Netscape 4 or IE Mac, neither of which are supported by any sane company these days.  Of course, now that Netscape 4 is finally settling into the dim recesses of history where it belongs, we still have all the flavors of IE to plague us.  Microsoft does their best work when they are playing catch-up.  Everyone predicted that as soon as they were the dominant browser they would stop caring about standards and stop updating their browser in any significant way.  Microsoft denied it, but can anyone really say they're suprised that it's exactly what they did?  Only when Firefox began to seriously erode their "market share" did they reanimate the moribund IE team.  I can't wait to see what kind of nightmares we get when that monster walks out of the laboratory.&lt;br /&gt;&lt;br /&gt;Despite all this, I'm still somewhat of a fan of Javascript.  It doesn't get much respect, in part because of poor implementations, and in part because it wasn't designed to be very modular. It ignores modern concepts such as threading.  You could go on for days on the problems of Javascript, and others have, so I won't.  What all this is getting to is that I'm excited about seeing good uses of Javascript in &lt;a href="http://maps.google.com/maps?q=Vancouver,bc,+canada&amp;spn=.130507,.324526&amp;hl=en"&gt;Google Maps&lt;/a&gt; and &lt;a href="http://backpackit.com/"&gt;Backpack&lt;/a&gt;.  I'm going to try using &lt;a href="http://script.aculo.us/"&gt;Script.aculo.us&lt;/a&gt; to see what I can get away with (using my new fave web framework, &lt;a href="http://www.cherrypy.org/"&gt;CherryPy&lt;/a&gt;).  And I'm really, really looking forward to what Bob Ippolito is cooking up with his semi-stealth project: &lt;a href="http://bob.pythonmac.org/archives/category/javascript/mochikit/"&gt;Mochikit&lt;/a&gt;. Bob has done some amazing work with PyObjC, and Mochikit could well be the Javascript framework I've been looking for, complete with documentation and unit tests.  I can't wait.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-112123211602654925?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/112123211602654925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=112123211602654925' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/112123211602654925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/112123211602654925'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/07/born-again-javascript.html' title='Born-Again Javascript'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-111881407697515607</id><published>2005-06-15T05:17:00.000Z</published><updated>2005-11-16T05:50:44.070Z</updated><title type='text'>My Spider</title><content type='html'>The spiders watch us, tend us, and observe everything.  Most people don't notice them or even pay attention, but it always creeps me out a little when they push their way back into spacetime, wriggling out of the web between the worlds. I don't like the way one will pause halfway, with the fabric of existence split open around it, its metaeyes gleaming as it scans my playa to take a snapshot of this herenow. Searching. Archiving. Somewhens it's no more than that, the spider pulls back down the timeseam, spacetime seals closed around the tear, and it was never herenow again.  Otherwhens, the spider comes out, letting spacetime close behind it, scans us, and modifies us if necessary.  The last time the spider came out, Magister presented as a man, but he used to present as a woman.  And Loli used to be a veteran of the razor wars, like I am, but now she isn't anymore.&lt;br /&gt;&lt;br /&gt;The spiders were named, as near as I can figure, on mythological creatures with eight legs, which had poison fangs, wove complex webs of high tension cables, and drank blood. In the old days people had pretty wild imaginations, or a lot of spacetime on their hands, I guess.&lt;br /&gt;&lt;br /&gt;The story goes that the first spiders were pure software, stalking their way across the early network, following trails, remembering for us, much like our spiders do, but much more superficially.  In those days people used computers. Their spiders existed only on the networks between the computers.  I can't even imagine a life so slow and relaxed.&lt;br /&gt;&lt;br /&gt;The spiders give us everything and take everything away, but the spiders are not perfect.  Even the spiders have faults.  No one else knows that.  Magister insists that there is only one spider, moving in and out of spacetime everywhen, but I know that's not true.  I know a lot of things that no one else knows, because I found the dead spider and took it back to my creche.  I took it and examined it, and learned how to ask it questions.  I'm still not good at asking questions, or understanding the answers it gives, but I'm getting better.&lt;br /&gt;&lt;br /&gt;I know there is more than one spider because the spiders still come, but my spider lies dead and hidden in my creche.  I must not let the other spiders scan me because then they will find the dead spider and take it away.  If they find the dead spider they will modify me and I will never have had my spider.  I will have to be a boy again, or a pilot with holo-plated eyes, or worse.  The me I know will never have been.  I have to ask my spider how to keep away from them, I have to make it protect me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-111881407697515607?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/111881407697515607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=111881407697515607' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111881407697515607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111881407697515607'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/06/my-spider.html' title='My Spider'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-111820971049567613</id><published>2005-06-07T17:30:00.000Z</published><updated>2005-06-08T05:48:30.526Z</updated><title type='text'>The Sky is Falling</title><content type='html'>Is it just me, or has the world gone crazy?&lt;br /&gt;&lt;br /&gt;First, Microsoft announces that the next version of Office will use XML file formats as the default.  Now, Apple announces that (after all these years of denial) they are moving to Intel chips.&lt;br /&gt;&lt;br /&gt;Vuja Dey (Vuja dey is the strange feeling you've never been here before).&lt;br /&gt;&lt;br /&gt;Years ago I was in Boston for MacWorld and the &lt;a href="http://www.beincorporated.com/"&gt;BeOS&lt;/a&gt; developer's conference when &lt;a href="http://www.bedope.com/qjlg/jlgquotes-sigs.txt.zip"&gt;Jean-Louis&lt;/a&gt; unveiled BeOS running on Intel processors (because Steve Jobs wouldn't give Be specs to the PowerPC Macs).  Yesterday's announcement felt a little like that (even a similar presentation, hmmm). Intel is *very* motivated to have something besides windows running on their chips--they loaned Be a couple of engineers for the port.  Of course, BeOS started on IBM "Hobbit" CPUs, then the PowerPC BeBox, then the PowerPC Mac, then Intel, now PalmPilots.  OS X started on Intel and wasn't ported to PowerPC until Apple bought them, so it's not as much of a stretch.  Now if Microsoft started to build stuff on PowerPC, *that* would be crazy.&lt;br /&gt;&lt;br /&gt;Oh, wait, &lt;a href="http://www.extremetech.com/article2/0,3973,1371393,00.asp"&gt;never mind&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The thing that really gets me is that I have to wait another year before I can buy a PowerBook with good battery life which doesn't sear my lap.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-111820971049567613?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/111820971049567613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=111820971049567613' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111820971049567613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111820971049567613'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/06/sky-is-falling.html' title='The Sky is Falling'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-111700034418288737</id><published>2005-05-25T05:27:00.000Z</published><updated>2005-05-25T05:52:24.186Z</updated><title type='text'>Tidbits</title><content type='html'>My posts have been infrequent, in part because I've been working on lots of things to talk about.  I'm in the last stages of putting together info on creating NSStatusItems (tools which show up in the menu bar across all applications in OS X) in PyObjC. I've also got some cool Quicktime and iSight tools coming soon.  And I've renamed ZenPaint to DrawingBoard, but it's working and just waiting for a little GUI cleanup before I post the first binary and source.&lt;br /&gt;&lt;br /&gt;Two of my back-burner projects, better blue-screening, and easy lightsabre effects, have been done by others recently.  Inspired by the same BoingBoing piece on rotoscoping your own lightsabres as I was, but &lt;a href="http://www.lamarchefamily.net/nakedsoft/"&gt;Naked Software&lt;/a&gt; actually sat down and wrote the code.  It's pretty slick, too.  For blue-screen effects (and many more), check out Sam Kass' Quartz Composer Compositions. Very neat stuff, Tiger-only though.  Some of the compositions require a newer system with a higher-end video card than my three-year-old PowerBook.&lt;br /&gt;&lt;br /&gt;But to be honest, the real point of this post is not to tease with coming attractions, but to point out my first paid publication. My friend &lt;a href="http://gnosis.cx/publish/"&gt;David Mertz&lt;/a&gt; asked me to collaborate with him on his &lt;a href="http://www-128.ibm.com/developerworks/views/xml/libraryview.jsp?search_by=xml+matters:"&gt;XML Matters&lt;/a&gt; column for IBM developerWorks, and my first column went live last Friday: &lt;a href="http://www-128.ibm.com/developerworks/xml/library/x-matters41.html"&gt;Beyond the DOM&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I've wanted to be a writer for as long as I can remember, with poetry notebooks and 200 pages of a novel gathering dust on my bookshelves, so finally getting around to finishing something and having it published leaves me pleased as punch. And more &lt;b&gt;will&lt;/b&gt; be forthcoming.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-111700034418288737?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/111700034418288737/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=111700034418288737' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111700034418288737'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111700034418288737'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/05/tidbits.html' title='Tidbits'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-111509917319586771</id><published>2005-05-03T05:42:00.000Z</published><updated>2005-05-03T05:46:13.196Z</updated><title type='text'>Happiness is a Warm Tiger</title><content type='html'>My copy of Mac OS 10.4 arrived Friday, right on schedule.  I spent the weekend backing up my laptop, doing a clean install, and then restoring my data, which also fixed the problem I've been having where applications take a really long time to launch. No problems so far and performance is noticeably faster.  You gotta love an OS where each update makes your existing hardware faster.&lt;br /&gt;&lt;br /&gt;First impressions: Spotlight searching really is as fast as they claim, Dashboard is neat in a gee-whiz sort of way, but I'm not sure how much I'll actually use it.  The built-in dictionary and thesaurus are welcome additions. I'm sure with time I will come to use smart folders in both the Finder and Mail.  But for me the real juice in this version is underneath the hood in the development tools.&lt;br /&gt;&lt;br /&gt;I've had a few secret hopes for Tiger, for things which have not been announced, but might be slipped under the door.  Three of them were: NSOutlineView gaining support for varying row height (to make it easier to write applications like OmniOutline), improved Cocoa support for QuickTime, and being able to round-trip Nibs to text format and back via nibtool.  Well, two out of three ain't bad.  The NSOutline now supports row height via it's delegate method &lt;a href=http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Classes/NSOutlineView.html#//apple_ref/doc/uid/20000110-BABHDHAE"&gt;heightOfRow:ofItem:&lt;/a&gt;, the &lt;a href="http://developer.apple.com/documentation/QuickTime/Reference/QTCocoaObjCKit/index.html#//apple_ref/doc/uid/TP40001164"&gt;QTKit&lt;/a&gt; framework provides excellent support for QuickTime media from Cocoa (and thus from PyObjC), but alas, nibs cannot be created from text input via &lt;a href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/nibtool.1.html"&gt;nibtool&lt;/a&gt; (or any other tool that I'm aware of), although the nibtool man page does at least list this deficiency as a known bug.&lt;br /&gt;&lt;br /&gt;But there is more good news in the &lt;a href="http://developer.apple.com/documentation/GraphicsImaging/Reference/CoreImagingRef/index.html"&gt;Core Image&lt;/a&gt;, &lt;a href="http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudio/book_intro/chapter_1_section_1.html"&gt;Core Audio&lt;/a&gt;, and &lt;a href="http://developer.apple.com/documentation/GraphicsImaging/Reference/CoreImagingRef/index.html"&gt;Core Data&lt;/a&gt; frameworks.  Core Image gives fast, powerful graphic processing and pipelining tools for both still images and video.  Core Audio does the same for sound.  And while Mac development in the Model-View-Controller (MVC) has been supported via Interface Builder (View), NSArrayController and NSObjectController (Controller), now with Core Data the Model portion is fully supported as well.  Bill Bumgarner has a welcome &lt;a href="http://www.pycs.net/bbum/2005/5/1/"&gt;example&lt;/a&gt; on his blog of how to use CoreData from Python. &lt;br /&gt;&lt;br /&gt;But wait, there's more!  There's an &lt;a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Classes/NSTreeController.html#//apple_ref/occ/cl/NSTreeController"&gt;NSTreeController&lt;/a&gt; to go along with the NSArrayController and friends.  There are hooks and documentation for many more of the Apple-supplied applications, including the new &lt;a href="http://developer.apple.com/documentation/Cocoa/Reference/SyncServicesRef_ObjC/index.html"&gt;Sync Services&lt;/a&gt;. And PyObjC now has wrappers for Core Data, Automator, XGrid, and Sync Services.  And that's not to mention the improved WebKit, new features of NSTextView, and much more.  It's a great time to be a Mac developer, and being able to do all this from Python really ices the cake for me.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-111509917319586771?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/111509917319586771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=111509917319586771' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111509917319586771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111509917319586771'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/05/happiness-is-warm-tiger.html' title='Happiness is a Warm Tiger'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-111397410963906847</id><published>2005-04-20T05:14:00.000Z</published><updated>2005-04-20T13:23:19.636Z</updated><title type='text'>Individuality for the Masses</title><content type='html'>&lt;p&gt;[via &lt;a href="http://www.boingboing.net/2005/04/18/individuali_a_peace_.html"&gt;BoingBoing&lt;/a&gt;]&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.individual-i.com/"&gt;http://www.individual-i.com/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.individual-i.com/"&gt;&lt;img src="http://www.individual-i.com/images/support-black.gif" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Bruce Schneier has come up with the idea of promoting individual rights via the awesome power of branding, as the peace symbol did for the anti-war movement.  I think it's a good idea, though the complexities can be harder to explain than "killing is wrong." (Of course, the anti-war movement was more complex as well, but the basic message seems easier to get across...).  Here's the gist from the site:&lt;/p&gt;&lt;blockquote&gt;&lt;strong&gt;Individual-i stands for:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt; Freedom from surveillance&lt;/li&gt;&lt;li&gt; Personal privacy&lt;/li&gt;&lt;li&gt; Anonymity&lt;/li&gt;&lt;li&gt; Equal protection&lt;/li&gt;&lt;li&gt; Due process&lt;/li&gt;&lt;li&gt; Freedom to read, write, think, speak, associate, and travel&lt;/li&gt;&lt;li&gt; The right to make your own choices about sex, reproduction, marriage, and death&lt;/li&gt;&lt;li&gt; The right to dissent&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;&lt;p&gt;These are all things I can support.  I hope this idea spreads like memefire.&lt;/p&gt;&lt;p&gt;It needs a hand gesture, though.  How about the sign-language alphabetic "I" held to the chest?&lt;/p&gt;&lt;a href="http://where.com/scott.net/asl/abc.html"&gt;&lt;img src="http://where.com/~templar/asl/i.GIF" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-111397410963906847?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/111397410963906847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=111397410963906847' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111397410963906847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111397410963906847'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/04/individuality-for-masses.html' title='Individuality for the Masses'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-111231848008959271</id><published>2005-04-01T01:20:00.000Z</published><updated>2006-05-01T05:19:18.250Z</updated><title type='text'>Now we're cooking!</title><content type='html'>My copy of the new Python Cookbook arrived today (pretty quick considering my first issue of Make magazine hasn't arrived yet). It's pretty cool to see my name in an O'Reilly book, even for only a single recipe.  The book looks great too, I think Alex and Anna and David have done a great job and I'm really looking forward to going through it.&lt;br /&gt;&lt;br /&gt;Thanks to everyone who replied about the missing word.  I've been a bit overwhelmed lately and haven't had a chance to follow up with that, but the responses were all great.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-111231848008959271?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/111231848008959271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=111231848008959271' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111231848008959271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111231848008959271'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/04/now-were-cooking.html' title='Now we&apos;re cooking!'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-111009461926340425</id><published>2005-03-06T07:35:00.000Z</published><updated>2005-03-06T07:36:59.263Z</updated><title type='text'>The Missing Word</title><content type='html'>I was trying to give an example of an obscure word to my wife and chose "perispacity."  Now, I'm not at all sure of my spelling of it, but that spelling turns up a few hits in Google.  I can't find it in any dictionary however, so I'm turning to the awesome power of the blogosphere.  Can anyone point me to a definition (and correct spelling)?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-111009461926340425?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/111009461926340425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=111009461926340425' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111009461926340425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/111009461926340425'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/03/missing-word.html' title='The Missing Word'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110995746140816899</id><published>2005-03-04T16:33:00.000Z</published><updated>2005-11-10T00:11:12.586Z</updated><title type='text'>50 Year Language</title><content type='html'>A post by &lt;a href="http://patricklogan.blogspot.com/2005/03/languaging.html"&gt;Patrick Logan&lt;/a&gt; on languages, in response to something &lt;a href="http://www.dehora.net/journal/2005/03/communication_languages.html"&gt;Bill de hÓra&lt;/a&gt; wrote on language and communication inspired me to think some more about the language I'd like to be programming in.  If you follow those links you might be interested to know that KIF is the &lt;a href="http://logic.stanford.edu/kif/dpans.html"&gt;Knowledge Interchange Format&lt;/a&gt;, FIPA is the Foundation for Intelligent Physical Agents, and ACL is the &lt;a href="http://www.fipa.org/specs/fipa00061/"&gt;Agent Communication Language&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Paul Graham has an essay, &lt;a href="http://www.paulgraham.com/hundred.html"&gt;The Hundred-Year Language&lt;/a&gt; in which he argues, in part, "Unlike physics in a hundred years, which is almost necessarily impossible to predict, I think it may be possible in principle to design a [programming] language now that would appeal to users in a hundred years."&lt;br /&gt;&lt;br /&gt;It's a hard argument to back up, because we have no evidence in our existing programming languages.  Lisp and Fortran are a little over fifty years old at this point, so those are the oldest examples we have to go by.  And only Lisp is still gaining new adherents, as far as I can tell.  It took Unix over 30 years to begin winning the OS wars, so who knows, maybe Lisp's time has come.&lt;br /&gt;&lt;br /&gt;There are some trends that have been showing up in recent mainstream languages like Java, Python, etc.  The ability to handle Unicode for internationalization.  Threads for scaling.  Making network connections easier, especially for HTTP connections.  Various layers of support for HTML.&lt;br /&gt;&lt;br /&gt;If we assume that adding these facilities into programming languages is progress, if it is a kind of encapsulation of best practices, or at least making common cases of complex behaviour more accessible (if you've ever managed network connections in C you'll understand what I mean), then what does that say for the next 50 year language?  What would a language with the staying power of Lisp look like?&lt;br /&gt;&lt;br /&gt;Well, what are the trends and emerging best practices today?  Testing is a big one.  Designing for testability, unit testing, test-driven development, these are all current buzzwords and for good reason.  If Extreme Programming manages to get one idea into the mainstream, test-driven development would be a good one.  But certain types of code are very hard to test, and these are some of the code that most needs to be tested.  Code that results in persistent state changes (databases) are hard to isolate for testing because by nature they keep changing.  Threaded code is hard to test, and multi-process code is even harder.  User interfaces are incredibly hard to test well.  Web code is difficult to test because you have no control over the environment it runs in--how do you automate a test which needs to run over several versions of several browsers under various operating systems?  Performance is difficult to test well, it's hard to isolate the bottlenecks of code to see where a minimum of effort (and disruption) can make a maximum performance impact.  &lt;br /&gt;&lt;br /&gt;So, one thing I'd like to see in the language of the future is the language designed for testability.  What would a language deigned for testing look like?  In part it might look like Eiffel with all the knobs turned to 11.&lt;br /&gt;&lt;br /&gt;We can look at some of the most heralded features of existing languages, some mainstream, some more esoteric, to see what else might go into this future language.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Scalability [Erlang]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Robustness [Erlang]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Testability [Eiffel]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Flexibility [Smalltalk/Lisp]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Support for Little Languages&lt; [Lisp]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Native UI [C#]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;XML as a native datatype [ECMAScript2]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Small core language [Ruby/Lua]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Web as a core competency [ECMAScript/PHP]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Refactorability [Smalltalk]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Prototyping [ECMAScript/Smalltalk]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Security [What does this now?]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Manipulate units as well as numbers [Frink]&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Able to compile efficiently when needed [Lisp]&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;The specific languages I chose are meant as examples, and reflect what I have some knowledge of.  If I've left out Haskell, or Oz, etc., it's largely because I'm less familiar with them and their benefits.  The languages I chose are definitely weighted towards the dynamic end of the spectrum because that's just the way things are going and I can only see that trend accelerating.&lt;br /&gt;&lt;br /&gt;I've also mixed up language and libraries a bit freely.  This will be one of the axes that languages will be measured on.  Python would probably be easier to port to new platforms if the language core were smaller and the standard library could be defined in terms of that core.  Lua wins in this regard, if someone wants a small, embeddable language.  Some of the list above may be mutually contradictory.  Paul Graham theorizes that there will be only a small number of languages in the future, but there will certainly be more than one, depending on the needs of the programmer.&lt;br /&gt;&lt;br /&gt;Smalltalk and Lisp are mentioned above as "Flexible" and I should probably mention what I mean by that.  In both languages you can create new core language features: operators, flow control, code rewriting, etc.  Most languages do not support that level of manipulation (and Java specifically goes to lengths to prevent it).&lt;br /&gt;&lt;br /&gt;When I think about what I'd like to see in the next fifty-year language, I call it Rotfl, which comes from ROTFL (Rolling on the floor laughing), which is what a programmming language should lead to.  I spend far too much of my coding time in computer-induced Tourette's Syndrome (i.e., swearing uncontrollably).  I want to put the fun back in functions.  This is at the core of Programming for the Fun of It.  Some people spell Rotfl as Python 3000.  We'll see.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110995746140816899?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110995746140816899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110995746140816899' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110995746140816899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110995746140816899'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/03/50-year-language.html' title='50 Year Language'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110931095802310411</id><published>2005-02-25T05:49:00.000Z</published><updated>2005-02-25T05:55:58.023Z</updated><title type='text'>Coming attractions</title><content type='html'>I'm slowly making progress on ZenPaint, a simple animation tool for kids.  I started writing it using PyGame, but hit the wall in terms of display, having to redefine my own widgets, etc.  So I've taken a couple of step back and now I'm working on it using PyObjC. I keep waffling back and forth on whether to use Interface Builder or not.  I don't find Interface Builder very intuitive at all, but maybe I need to buckle down and get good at it.  Knowing about nibtool helps--at least I can do text diffs and simple renames from the command-line.&lt;br /&gt;&lt;br /&gt;One thing I would like to point out, the Apple &lt;a href="http://developer.apple.com/releasenotes/Cocoa/NSDocumentFAQ.html"&gt;NSDocument FAQ&lt;/a&gt; is one of the best bits of documentation I've ever seen out of Apple.  So I'm mentally translating it into Python and trying to apply it to ZenPaint.  As soon as I can load and save files reliably I'll post the program and code up here for comments.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110931095802310411?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110931095802310411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110931095802310411' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110931095802310411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110931095802310411'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/02/coming-attractions.html' title='Coming attractions'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110874788532757164</id><published>2005-02-18T17:13:00.000Z</published><updated>2005-02-18T17:31:48.603Z</updated><title type='text'>Hearing Voices</title><content type='html'>&lt;a href="http://northernvoice-ca.canadawebhosting.com/"&gt;Northern Voices&lt;/a&gt;, that is.  The local blogging conference is tomorrow and I'll be there, along with my friend and co-worker, &lt;a href="http://www.theirishkid.com/blog/"&gt;Michael&lt;/a&gt;.  While I find the idea of a conference about blogging to be a bit odd, it's a good chance to meet some of the people in person who I only know from blogspace, like &lt;a href="http://www.sauria.com/blog"&gt;Ted&lt;/a&gt; and &lt;a href="http://www.julieleung.com/"&gt;Julie&lt;/a&gt;, and a chance to catch up with &lt;a href="http://tbray.org/ongoing/"&gt;Tim&lt;/a&gt; and &lt;a href="http://www.laurenwood.org/anyway/"&gt;Lauren&lt;/a&gt;, who I almost never see even though we live in the same town.&lt;br /&gt;&lt;br /&gt;Should be a good time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110874788532757164?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110874788532757164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110874788532757164' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110874788532757164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110874788532757164'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/02/hearing-voices.html' title='Hearing Voices'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110866253334010024</id><published>2005-02-17T17:32:00.000Z</published><updated>2005-02-17T22:50:56.746Z</updated><title type='text'>Why Macintosh?</title><content type='html'>I admit it, I'm a bit of a Mac bigot.  What I'd like to talk about briefly is why.  I've used Macintoshes since the very early days of the 512K Mac and enjoyed the experience mostly.  I've also used DOS extensively, Windows since Windows 2.0, many flavors of unix and linux, BeOS, Commodores, and the venerable TRS-80.  I've left out a few, for brevity.  The only one which came close to being as day-to-day &lt;em&gt;usable&lt;/em&gt;&lt;br /&gt;for me as a Mac was BeOS, and that didn't work out because a) they never achieved good Java support (which used to be quite important to me), and b) they went out of business (although their DNA lives on in PalmOS, and their influence is quite active in both Linux and OS X).  So I keep coming back to the Mac.  At work I use Windows because I have to.  At home I use a Mac because I enjoy it.&lt;br /&gt;&lt;br /&gt;What is so different?  Over the years Windows and Linux have become more Mac-like to the point where the differences are less grating.  To some it is as if the differences have been erased, but that isn't so (on many levels--the programming experience is also quite different on different platforms).  The same guys who sneered at the Mac in the old days because "real men use DOS and don't need pretty fonts or mice" now sneer at the Mac because "real men use Windows and can't live with one-button mice."  I'm only exaggerating a little here, I've really had conversations along those lines.&lt;br /&gt;&lt;br /&gt;Here's the deal:  Computers aren't really ready yet.  They haven't been made usable for real people and are only fit for extreme geeks with a lot of time to kill.  The past decade or more has seen more backsliding than progress on this front.  Of course, computers are cheap now, and can be made to do useful work if you are willing to try hard enough, and they're sexy toys, so everyone buys them and uses them.  Some progress to make them more useful has happened (email and the web, mainly), but what progress has been made has been mostly in off-the-clock, non-authorized ways.&lt;br /&gt;&lt;br /&gt;One of the very few companies to at least &lt;em&gt;try&lt;/em&gt; to make computers more usable by normal people has been Apple.  They don't always succeeed, but they consistently try, and they consistently do better than the rest.  By a large and growing margin.&lt;br /&gt;&lt;br /&gt;I'm happy to see Linux becoming more usable as a desktop computer.  I use Linux and I recommend it for many situations.  And frankly, I don't care much for Steve Jobs as a person and feel kind of dirty giving him free advertising like this. But when I focus my time, my development efforts, and my creativity, I want to focus on the best.  And right now that's OS X.  It just is.&lt;br /&gt;&lt;br /&gt;For more along this lines, see Ian Bicking's &lt;a href="http://blog.ianbicking.org/did-he-mention-workflow.html"&gt;response&lt;/a&gt; to JWZ's highly-linked rant against &lt;a href="http://www.jwz.org/doc/groupware.html"&gt;"groupware"&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110866253334010024?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110866253334010024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110866253334010024' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110866253334010024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110866253334010024'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/02/why-macintosh.html' title='Why Macintosh?'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110805816407886951</id><published>2005-02-10T17:51:00.000Z</published><updated>2005-02-10T17:56:04.076Z</updated><title type='text'>Slides Up</title><content type='html'>After much delay, my slides are finally up from the VanPyZ talk last week.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://livingcode.org/slides/cocoa.html"&gt;Using Python and Cocoa on OS X&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Again I'm using Eric Meyer's S5 tool for HTML slides, but it still ends up being a large download because it includes a completely unneccesary quicktime movie.  My daughter and I have been playing with iStopMotion and this was one of our first forays into claymation.&lt;br /&gt;&lt;br /&gt;The reason it's in the slideshow, is that movie making is now completely accessible to an eight-year-old, and I want to writing games and other programs equally accessible to her.&lt;br /&gt;&lt;br /&gt;Still a ways to go...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110805816407886951?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110805816407886951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110805816407886951' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110805816407886951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110805816407886951'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/02/slides-up.html' title='Slides Up'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110799008968532149</id><published>2005-02-09T22:43:00.000Z</published><updated>2006-03-23T00:03:00.710Z</updated><title type='text'>Is that a banana in your email or are you happy to see me?</title><content type='html'>I've gotten a couple of messages asking why there are strange attachments in my email which may or may not prompt you to open your security settings if you try to open them.  Fear not, these are not viruses!  Rather, it's the basis of email itself if we ever want to move beyond the ocean of spam we currently swim in.&lt;br /&gt;&lt;br /&gt;First some background.  When you send an email, by default there are three things which *do not* occur.  The first is Authorization, i.e., there is no guarantee that the person you are sending the mail to is able to read it.  The second is Encryption, which ensures that other people are not able to read it.  The third is Authentication, proving that the mail which appears to be from me, actually is from me.  None of these are present in normal email.&lt;br /&gt;&lt;br /&gt;In fact, sending an email without these is like putting all of your standard (snail) mail on postcards, then having the postcards travel through the houses of random strangers until they (possibly) end up at their destination.  Adding authentication, authorization, and encryption is like putting your email in an envelope.&lt;br /&gt;&lt;br /&gt;So that is what the obscure attachment is in my email, it's a digital signature, which provides some form of authentication (better than nothing).  I recieved this certificate from a company called Thawte, for free, and installed it in my primary email application, Mail.app.  There are very good instructions for doing this at &lt;a href="http://www.joar.com/certificates/"&gt;http://www.joar.com/certificates/&lt;/a&gt;.  Once you have installed the certificate, signing your mail happens authomatically and transparently.  The process of getting the certificate could be easier, but it's certainly not difficult.&lt;br /&gt;&lt;br /&gt;The real benefit happens when more people start doing this.  See, if I have received mail from someone who also uses digital signatures, then Mail.app remembers this, and when I respond it uses their signature and my signature in combination to get the rest of the picture: Authorization and Encryption.  If we can get to the point where the standard is to put messages in an envelope instead of postcards, it makes much better tools for eliminating spam available--don't accept mail unless it is from a verifiable source.  It's not perfect, but a big improvement over the situation we have today.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110799008968532149?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110799008968532149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110799008968532149' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110799008968532149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110799008968532149'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/02/is-that-banana-in-your-email-or-are.html' title='Is that a banana in your email or are you happy to see me?'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110798895840220550</id><published>2005-02-09T22:31:00.000Z</published><updated>2005-02-10T00:31:04.136Z</updated><title type='text'>Big 40</title><content type='html'>I noticed over on &lt;a href="http://www.planetpython.org/"&gt;Planet Python&lt;/a&gt; that &lt;a href="http://radio.weblogs.com/0100039/"&gt;David Brown&lt;/a&gt; (who I only know of from blogspace, as opposed to &lt;a href="http://www.dagbrown.com"&gt;Dave Brown&lt;/a&gt;, who I know from meatspace), just turned 40.  Congratulations, David!  I turned 40 last Friday, so happy birthday to me as well.  &lt;br /&gt;&lt;br /&gt;As the geek joke tells us, don't worry, next year we'll be back in our primes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110798895840220550?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110798895840220550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110798895840220550' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110798895840220550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110798895840220550'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/02/big-40.html' title='Big 40'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110729551361772852</id><published>2005-02-01T22:01:00.000Z</published><updated>2005-02-01T22:05:13.616Z</updated><title type='text'>No mirrors allowed</title><content type='html'>Just a reminder that tonight at 7:00 I'll be speaking at &lt;a href="http://www.vanpyz.org/news"&gt;VanPyZ&lt;/a&gt; (Vancouver Python and Zope users group).  I also had surgery today on my foot to remove a cyst, so I'll be presenting while sitting with my foot in the air.  I hope that's not too distracting.  The subject, of course, is how to use Python to make programming OS X easier.  Hope to see you there!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110729551361772852?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110729551361772852/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110729551361772852' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110729551361772852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110729551361772852'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/02/no-mirrors-allowed.html' title='No mirrors allowed'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110698004829958672</id><published>2005-01-29T05:38:00.000Z</published><updated>2005-01-29T06:30:39.666Z</updated><title type='text'>Extending NSBezierPath</title><content type='html'>Yesterday I wrote about how to extend NSImage so it can save to a file.  Today we'll tackle NSBezierPath.  NSBezierPath is pretty cool for drawing, but it doesn't support arbitrary regular polygons, just rects and ovals (and lines and arcs).  And there's not an easy way to extract the points that make up a path.  And if you could extract the points, there isn't a way to draw dots for the points instead of stroking or filling the path.  OK, enough already, let's look at some code.&lt;br /&gt;&lt;br /&gt;First thing in the code, we'll define some basic trigonometry routines to calculate the points for a polygon.  Then we'll create the class itself.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;from objc import Category&lt;br /&gt;from AppKit import NSBezierPath&lt;br /&gt;import math&lt;br /&gt;&lt;br /&gt;def poly_point(center, r, degrees):&lt;br /&gt;    x = r * math.cos(degrees) + center[0]&lt;br /&gt;    y = r * math.sin(degrees) + center[1]&lt;br /&gt;    return x,y&lt;br /&gt;&lt;br /&gt;def polypoints(center, r, numPoints, degreesRotation=0):&lt;br /&gt;    if numPoints &amp;lt; 3: raise ValueError, 'Must have at least 3 points in a polygon'&lt;br /&gt;    rotation = math.radians(degreesRotation)&lt;br /&gt;    theta = (math.pi * 2) / numPoints&lt;br /&gt;    return [poly_point(center, r, i*theta+rotation) for i in range(numPoints)]&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;class NSBezierPath(Category(NSBezierPath)):&lt;br /&gt;&lt;br /&gt;    def points(self):&lt;br /&gt;        points = []&lt;br /&gt;        for i in range(self.elementCount()):&lt;br /&gt;            elem, pts = self.elementAtIndex_associatedPoints_(i)&lt;br /&gt;            points += pts&lt;br /&gt;        return points&lt;br /&gt;&lt;br /&gt;    def appendBezierPathWithPolygonWithCenter_radius_numberOfPoints_(self, center, radius, numberOfPoints):&lt;br /&gt;        '''&lt;br /&gt;        Creates a regular polygon&lt;br /&gt;        '''&lt;br /&gt;        pts = polypoints(center, radius, numberOfPoints)&lt;br /&gt;        self.moveToPoint_(pts[0])&lt;br /&gt;        for pt in pts[1:]:&lt;br /&gt;            self.lineToPoint_(pt)&lt;br /&gt;        self.closePath()&lt;br /&gt;&lt;br /&gt;    def dot(self):&lt;br /&gt;        '''&lt;br /&gt;        Similar to stroke: and fill:, but draws dots for each point in the&lt;br /&gt;        path.  Dot size is based on linewidth. Not as efficient, because it&lt;br /&gt;        creates a separate NSBezierPath each time it is called.&lt;br /&gt;        '''&lt;br /&gt;        tmp_path = NSBezierPath.alloc().init()&lt;br /&gt;        width = self.lineWidth()&lt;br /&gt;        offset = width / 2&lt;br /&gt;        for point in self.points():&lt;br /&gt;            rect = (point[0] - offset, point[1] - offset),(width, width)&lt;br /&gt;            tmp_path.appendBezierPathWithOvalInRect_(rect)&lt;br /&gt;        tmp_path.fill()&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;br /&gt;OK, hopefully the above is reasonably clear.  You can follow along with any calls which are unfamiliar by firing up AppKiDo or the Apple documentation for &lt;a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/ObjC_classic/Classes/NSBezierPath.html#//apple_ref/occ/cl/NSBezierPath"&gt;NSBezierPath&lt;/a&gt;.  If you're going to use the dot: method a lot you might want to cache the path so you're not creating a new NSBezierPath every time, it depends on what you need.&lt;br /&gt;&lt;br /&gt;Here's a short script you can run on the command line to create a hexagon and demonstrate fill:, stroke: and dot:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;from AppKit import NSApplication, NSBezierPath, NSColor, NSImage&lt;br /&gt;from Foundation import NSInsetRect, NSMakeRect&lt;br /&gt;import image_ext, bezier_path_ext&lt;br /&gt;&lt;br /&gt;app = NSApplication.sharedApplication()&lt;br /&gt;&lt;br /&gt;image = NSImage.alloc().initWithSize_((64,64))&lt;br /&gt;image.fillWithColor_(NSColor.clearColor())&lt;br /&gt;image.lockFocus()&lt;br /&gt;hex = NSBezierPath.alloc().init()&lt;br /&gt;hex.appendBezierPathWithPolygonWithCenter_radius_numberOfPoints_((32,32), 26, 6)&lt;br /&gt;NSColor.greenColor().set()&lt;br /&gt;hex.fill()&lt;br /&gt;hex.setLineWidth_(2)&lt;br /&gt;NSColor.blueColor().set()&lt;br /&gt;hex.stroke()&lt;br /&gt;hex.setLineWidth_(8)&lt;br /&gt;NSColor.redColor().set()&lt;br /&gt;hex.dot()&lt;br /&gt;image.unlockFocus() &lt;br /&gt;image.writeToFilePath_('hex.png')&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Which results in this: &lt;img src="http://livingcode.org/images/hex1.png"/&gt;&lt;br /&gt;&lt;br /&gt;Why am I so interested in points and dots?  Well, they let me visualize control points for arcs for one thing.  Perhaps tomorrow we can explore more along those lines.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110698004829958672?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110698004829958672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110698004829958672' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110698004829958672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110698004829958672'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/01/extending-nsbezierpath.html' title='Extending NSBezierPath'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110689221680686440</id><published>2005-01-28T05:45:00.000Z</published><updated>2005-01-28T06:04:14.826Z</updated><title type='text'>Extending NSImage</title><content type='html'>I'm going to try to post smaller snippets and experiments, more frequently.  I've been struggling with creating images in code for a game I'm working on and had some successes lately that I wanted to share.  Along the way we get to play with Categories, which allow existing Cocoa classes to be extended with new methods at runtime without access to the source code.  Categories are in the latest release of PyObJC, but the example I'm giving uses a fresh checkout from the Subversion repository because I turned up a bug which Ronald Oussoren was kind enough to identify and immediately fix.  Since this example is already on the bleeding edge, I'll also dabble with Python2.4 descriptors.&lt;br /&gt;&lt;br /&gt;The specific problem I was faced with is that it is easy enough to create images programmatically, or to read them in from a file, but there was no obvious way to save them to a file.  A bit of googling led me to &lt;a href="http://borkware.com/quickies/"&gt;Mark Dalrymple's quickies&lt;/a&gt; which has lots of tips and tricks for programming Cocoa in Objective-C and which I got the basics for writing to an image to a file from.  I pythonified it and turned it into a method of NSImage through the magic of Categories, adding a bit more along the way.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;image_ext.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;from objc import Category&lt;br /&gt;from AppKit import *&lt;br /&gt;from os.path import splitext&lt;br /&gt;&lt;br /&gt;_fileRepresentationMapping = {&lt;br /&gt;        '.png': NSPNGFileType,&lt;br /&gt;        '.gif': NSGIFFileType,&lt;br /&gt;        '.jpg': NSJPEGFileType,&lt;br /&gt;        '.jpeg': NSJPEGFileType,&lt;br /&gt;        '.bmp': NSBMPFileType,&lt;br /&gt;        '.tif': NSTIFFFileType,&lt;br /&gt;        '.tiff': NSTIFFFileType,&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;def _getFileRepresentationType(filepath):&lt;br /&gt;    base, ext = splitext(filepath)&lt;br /&gt;    return _fileRepresentationMapping[ext.lower()]&lt;br /&gt;&lt;br /&gt;class NSImage(Category(NSImage)):&lt;br /&gt;&lt;br /&gt;    def rect(self):&lt;br /&gt;        return (0,0),self.size()&lt;br /&gt;&lt;br /&gt;    # If you're using the current release of PyObjC and don't feel like grabbing the fix&lt;br /&gt;    # from the repository, remove this method altogether and read in from files as&lt;br /&gt;    # usual (reading isn't so tricky)&lt;br /&gt;    @classmethod  # If you're using Python 2.3 comment this line and uncomment below&lt;br /&gt;    def imageWithFilePath_(cls, filepath):&lt;br /&gt;        return NSImage.alloc().initWithContentsOfFile_(filepath)&lt;br /&gt;    #imageWithFilePath_ = classmethod(imageWithFilePath_)&lt;br /&gt;&lt;br /&gt;    def writeToFilePath_(self, filepath):&lt;br /&gt;        self.lockFocus()&lt;br /&gt;        image_rep = NSBitmapImageRep.alloc().initWithFocusedViewRect_(self.rect())&lt;br /&gt;        self.unlockFocus()&lt;br /&gt;        representation = _getFileRepresentationType(filepath)&lt;br /&gt;        data = image_rep.representationUsingType_properties_(representation, None)&lt;br /&gt;        data.writeToFile_atomically_(filepath, False)&lt;br /&gt;&lt;br /&gt;    def fillWithColor_(self, color):&lt;br /&gt;        self.lockFocus()&lt;br /&gt;        color.set()&lt;br /&gt;        NSBezierPath.fillRect_(self.rect())&lt;br /&gt;        self.unlockFocus()&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I probably should have just elided the imageWithFilePath: method, since it is the only part which is really bleeding-edge, but I was so happy to get it working that I couldn't bring myself to drop it.  In any case, it's the writeToFilePath: method which I was looking for.  In case its not obvious, this will grab the extension of the path you pass in and determine the right type of file to save.  The key to saving images in Cocoa is that they have to pass through a subclass of NSImageRep and of NSData before they're ready to write.  This just encapsulates is all in one place.&lt;br /&gt;&lt;br /&gt;While I was at it I added fillWithColor: because I thought and image should be able to do that %-)&lt;br /&gt;&lt;br /&gt;Next up: Teaching NSBezierPath new tricks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110689221680686440?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110689221680686440/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110689221680686440' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110689221680686440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110689221680686440'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/01/extending-nsimage.html' title='Extending NSImage'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110620302047050763</id><published>2005-01-20T06:15:00.000Z</published><updated>2005-01-20T06:37:00.470Z</updated><title type='text'>Not dead yet</title><content type='html'>I'm still here.  I'm going to post the previous examples .dmg with some corrections pointed out by Bob Ippolito (4-space indents, don't modify data directly in the Bundle).  I've been quiet because a) I've been hitting some walls with Renaissance and investigating work-arounds and alternatives, and b) my coding/blogging time is pretty much between the time I get the kids to bed and the time my wife comes home from tutoring.&lt;br /&gt;&lt;br /&gt;I'm investigating the PyGUI and Wax APIs, to see if they are worth porting to run on top of Cocoa (PyGUI runs on Carbon, Wax runs on top of wxPython).  Both are attempts to make GUI creation more "Pythonic," which is a &lt;strong&gt;Good Thing&lt;sup&gt;&amp;trade;&lt;/sup&gt;&lt;/strong&gt;. I have figured out how to get the menus initialized using pure Python (on top of PyObjC, of course), or maybe the newer pyobjc/py2app has fixed the problem, but it is possible to build applications in Python with no Nib file (or Renaissance .gsmarkup file) at all.  My earlier inabillity to do that is what drove me to Renaissance in the first place.&lt;br /&gt;&lt;br /&gt;I've also discovered the nibtool utility, which I did not know about.  This allows you to see a textual representation of the nibs created by Interface Builder, search and replace strings (class names, etc.).  This is a major discovery.  Now if you could take the textual representation and put it back...  I'm going to have to investigate this further.&lt;br /&gt;&lt;br /&gt;In other news, I will be giving a presentation on Tuesday, February 1 at the Vancouver Zope and Python Users Group (&lt;a href="http://www.vanpyz.org/news"&gt;VanPyZ&lt;/a&gt;) at 7:00 p.m.  It will be a variation on the talk I gave in December to the XML users group, updated with what I've been exploring since then.  Specifically I will show a simple (Hello World) application built three different ways, with Renaissance, with Interface Builder, and in pure Python.  I'll also show some apps written in other toolkits (wxPython, tkinter) for comparison.  I hope some of my readers are close enough to make it.&lt;br /&gt;&lt;br /&gt;I'll also be attending the &lt;a href="http://www.northernvoice.ca/"&gt;Northern Voice&lt;/a&gt; blogging conference here in Vancouver on Saturday, February 19th.  I'm looking forward to meeting some fellow bloggers face to face, rather than RSS to RSS.&lt;br /&gt;&lt;br /&gt;Finally, I managed to install Python 2.4 today, and so far nothing has been obviously screwed up, so I'll be exploring some of the crunchy new features here in the near future.&lt;br /&gt;&lt;br /&gt;More posts coming soon.  Honest!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110620302047050763?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110620302047050763/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110620302047050763' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110620302047050763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110620302047050763'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2005/01/not-dead-yet.html' title='Not dead yet'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110334948282594567</id><published>2004-12-18T05:47:00.000Z</published><updated>2004-12-18T06:01:50.686Z</updated><title type='text'>Slides and code posted</title><content type='html'>My talk at &lt;a href="http://vanx.org/"&gt;VanX&lt;/a&gt; last night was a success.  Not many people showed (too close to Xmas), but the ones who did seemed to enjoy my presentation on Cocoa, Python, and Renaissance. Gerald was a great host, &lt;a href="http://schemasoft.com"&gt;SchemaSoft&lt;/a&gt; provided a good (and convenient!) venue, &lt;a href="http://prescod.net/"&gt;Paul&lt;/a&gt; came to show support, and &lt;a href="http://blastradius.com/"&gt;BlastRadius&lt;/a&gt; (my employer) sponsored the snacks.  After we went to Mira in Yaletown for beers and tech talk, had the whole place to ourselves, and a musician playing Flamenco guitar, then I walked across the bridge to home.  A great end to a great evening.&lt;br /&gt;&lt;br /&gt;I've posted my slides at &lt;a href="http://livingcode.org/slides/renaissance.html"&gt;livingcode.org/slides/renaissance.html&lt;/a&gt; using Eric Meyer's neat S&lt;sup&gt;5&lt;/sup&gt; tool to put the whole slideshow into one HTML file.&lt;br /&gt;&lt;br /&gt;I've also built a new .dmg containing all the source code and built, runnable examples (seven of them now), although they're not all fully functional yet.  More example gsmarkup to view, improved setup scripts, and they all have icons now.  The file is &lt;a href="http://sourceforge.net/project/showfiles.php?group_id=19654"&gt;examples 2004-12-17&lt;/a&gt; on SourceForge.&lt;br /&gt;&lt;br /&gt;I've been working hard to put this all together, which hasn't left much time for posting, but I will have a lot more to say real soon now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110334948282594567?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110334948282594567/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110334948282594567' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110334948282594567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110334948282594567'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/12/slides-and-code-posted.html' title='Slides and code posted'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110127610063058000</id><published>2004-11-24T04:49:00.000Z</published><updated>2004-11-24T06:25:00.350Z</updated><title type='text'>Oblique Strategies</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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 &lt;a href="http://www.rtqe.net/ObliqueStrategies/"&gt;Oblique Strategies Web Site&lt;/a&gt; has more details.  You can buy the deck from &lt;a href="http://www.enoshop.co.uk/"&gt;Brian Eno's site&lt;/a&gt;.  And a group called curvedspace created a nice &lt;a href="http://www.curvedspace.org/software/oblique.html"&gt;OS X version&lt;/a&gt; which you can download for free from their site.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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 &lt;a href="http://prdownloads.sourceforge.net/livingcode/ObliqueStrategies.dmg?download"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;What's new?  First of all, the &lt;code&gt;setup.py&lt;/code&gt; 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.&lt;br /&gt;&lt;br /&gt;In the &lt;code&gt;MainMenu.gsmarkup&lt;/code&gt; 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-&gt;Copy, but those menu items didn't have to change.&lt;br /&gt;&lt;br /&gt;In &lt;code&gt;oblique.py&lt;/code&gt;, 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 &lt;code&gt;instanceOf&lt;/code&gt; attribute of &amp;lt;window/&amp;gt; to call our subclass in the MainWindow.gsmarkup.  &lt;br /&gt;&lt;br /&gt;Our AppDelegate is similar to earlier examples, but has grown a couple of methods.  The &lt;code&gt;change()&lt;/code&gt; 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 &lt;code&gt;chooseFile_()&lt;/code&gt; method checks to see which menu item called it, and based on the menu item, selects the file for future calls to &lt;/code&gt;change()&lt;/code&gt;.  There is one support function, &lt;code&gt;getQuote(filename)&lt;/code&gt; which uses Python standard file manipulation and the random module to pick a quote (much less verbose than doing this from Objective-C).&lt;br /&gt;&lt;br /&gt;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!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110127610063058000?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110127610063058000/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110127610063058000' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110127610063058000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110127610063058000'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/oblique-strategies.html' title='Oblique Strategies'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110015183348819678</id><published>2004-11-11T02:57:00.000Z</published><updated>2004-11-11T05:43:53.490Z</updated><title type='text'>Pre-built examples</title><content type='html'>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 &lt;a href="http://sourceforge.net/projects/livingcode"&gt;Living Code&lt;/a&gt; project on SourceForge.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Mac OS 10.3 only.  Feedback appreciated.  Coming soon: applications that I can post meaningful screenshots of.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110015183348819678?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110015183348819678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110015183348819678' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110015183348819678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110015183348819678'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/pre-built-examples.html' title='Pre-built examples'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110007005756642705</id><published>2004-11-10T06:11:00.000Z</published><updated>2005-11-07T03:00:52.636Z</updated><title type='text'>Packaging Frameworks</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Old __init__.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;import objc, AppKit, Foundation&lt;br /&gt;objc.loadBundle('Renaissance', globals(), bundle_path='/Library/Frameworks/Renaissance.framework')&lt;br /&gt;del objc, AppKit, Foundation&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And the new, smarter version, which handles bundling.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;__init__.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;import objc, AppKit, Foundation, os&lt;br /&gt;if 'site-packages.zip' in __file__:&lt;br /&gt;  base_path = os.path.join(os.path.dirname(os.getcwd()), 'Frameworks')&lt;br /&gt;else:&lt;br /&gt;  base_path = '/Library/Frameworks'&lt;br /&gt;bundle_path = os.path.abspath(os.path.join(base_path, 'Renaissance.framework'))&lt;br /&gt;objc.loadBundle('Renaissance', globals(), bundle_path=bundle_path)&lt;br /&gt;del objc, AppKit, Foundation, os, base_path, bundle_path&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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&lt;br /&gt;&lt;br /&gt;&lt;code&gt;python setup.py py2app --framework=Renaissance.framework&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;If you use a python module that links to Renaissance, it will automatically get included.  Otherwise, you have to specify it as a framework.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;% python setup.py py2app -f Renaissance.framework&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;(you can specify a full path to the framework or the dylib inside the framework if you want)&lt;br /&gt;&lt;br /&gt;or from setup.py it would look like:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;setup(&lt;br /&gt;	app = [...],&lt;br /&gt;	options = dict(py2app=dict(&lt;br /&gt;		frameworks=['Renaissance.framework'],&lt;br /&gt;	)),&lt;br /&gt;)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;This command is your friend:&lt;br /&gt;&lt;code&gt;% python setup.py --help py2app&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;setup.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;'''&lt;br /&gt;Smarter setup.py example, run with:&lt;br /&gt;% python setup.py py2app&lt;br /&gt;'''&lt;br /&gt;&lt;br /&gt;from distutils.core import setup&lt;br /&gt;import py2app&lt;br /&gt;setup(&lt;br /&gt;  data_files = ['MainMenu.gsmarkup'],&lt;br /&gt;  app = ['browser.py',],&lt;br /&gt;  options=dict(py2app=dict(frameworks=['Renaissance.framework'],)),&lt;br /&gt;)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110007005756642705?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110007005756642705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110007005756642705' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110007005756642705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110007005756642705'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/packaging-frameworks.html' title='Packaging Frameworks'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-110005543877687160</id><published>2004-11-10T02:42:00.000Z</published><updated>2006-03-17T09:59:12.990Z</updated><title type='text'>Housekeeping</title><content type='html'>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 &lt;a href="http://sourceforge.net/projects/livingcode/"&gt;Living Code&lt;/a&gt; project, in the somewhat predictable cvs module, &lt;a href="http://cvs.sourceforge.net/viewcvs.py/livingcode/renaissance_examples/"&gt;renaissance_examples&lt;/a&gt;.  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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Then back to the Small Nerd examples and a couple of other applications (ports of existing tools, nothing terribly original yet).  &lt;br /&gt;&lt;br /&gt;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&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-110005543877687160?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/110005543877687160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=110005543877687160' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110005543877687160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/110005543877687160'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/housekeeping.html' title='Housekeeping'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-109997777431240870</id><published>2004-11-08T04:04:00.000Z</published><updated>2004-11-09T05:39:28.433Z</updated><title type='text'>GMarkup Browser</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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-&gt;Open menu or Cmd-O.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;MainMenu.gsmarkup&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE gsmarkup&amp;gt;&lt;br /&gt;&amp;lt;gsmarkup&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;objects&amp;gt;&lt;br /&gt;  &amp;lt;menu type="main"&amp;gt;&lt;br /&gt;    &amp;lt;menu title="GSMarkup Browser" type="apple"&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="About GSMarkup Browser" &lt;br /&gt;         action="orderFrontStandardAboutPanel:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menu title="Services" type="services"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Hide GSMarkup Browser" action="hide:" key="h"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Hide Others" action="hideOtherApplications:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Show All" action="unhideAllApplications:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Quit GSMarkup Browser" action="terminate:" key="q"/&amp;gt;&lt;br /&gt;    &amp;lt;/menu&amp;gt;&lt;br /&gt;    &amp;lt;menu title="File"&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Open" action="open:" key="o"/&amp;gt;&lt;br /&gt;    &amp;lt;/menu&amp;gt;&lt;br /&gt;    &amp;lt;menu title="Window" type="windows"&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Minimize Window" action="performMiniaturize:" key="m"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Bring All to Front" action="arrangeInFront:"/&amp;gt;&lt;br /&gt;    &amp;lt;/menu&amp;gt;&lt;br /&gt;  &amp;lt;/menu&amp;gt;&lt;br /&gt;&amp;lt;/objects&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/gsmarkup&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;browser.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;'''&lt;br /&gt;Port of GSMarkupBrowser from Objective-C to Python&lt;br /&gt;'''&lt;br /&gt;&lt;br /&gt;from Foundation import *&lt;br /&gt;from AppKit import *&lt;br /&gt;from Renaissance import *&lt;br /&gt;&lt;br /&gt;class Owner(NSObject):&lt;br /&gt;  &lt;br /&gt;  def takeValue_forKey_(self, obj, key):&lt;br /&gt;    #print 'Set value %s for key %s of NSOwner' % (obj, key)&lt;br /&gt;    pass&lt;br /&gt;&lt;br /&gt;  def bundleDidLoadGSMarkup_(self, notification):&lt;br /&gt;    if NSUserDefaults.standardUserDefaults().boolForKey_('DisplayAutoLayout'):&lt;br /&gt;      topLevelObjects = notification.userInfo().objectForKey_('NSTopLevelObjects')&lt;br /&gt;      for obj in topLevelObjects:&lt;br /&gt;        if obj.isKindOfClass_(NSWindow) or obj.isKindOfClass_(NSView):&lt;br /&gt;          obj.setDisplayAutoLayoutContainers_(True)&lt;br /&gt;&lt;br /&gt;  def applicationDidFinishLaunching_(self, notification):&lt;br /&gt;    self.open_(self)&lt;br /&gt;&lt;br /&gt;  def open_(self, notification):&lt;br /&gt;    filetypes = ['gsmarkup']&lt;br /&gt;    panel = NSOpenPanel.openPanel()&lt;br /&gt;    result = panel.runModalForDirectory_file_types_(None, None, filetypes)&lt;br /&gt;    if result == NSOKButton:&lt;br /&gt;      self.pathname = panel.filenames()[0]&lt;br /&gt;      #print 'Loading', self.pathname&lt;br /&gt;      didLoad = NSBundle.loadGSMarkupFile_externalNameTable_withZone_localizableStringsTable_inBundle_(self.pathname, &lt;br /&gt;{'NSOwner': self}, None, None, None)&lt;br /&gt;      if didLoad:&lt;br /&gt;        print self.pathname, 'loaded!'&lt;br /&gt;      else:&lt;br /&gt;        #print 'Could not load', self.pathname&lt;br /&gt;        NSBeep()&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;  defaults = NSUserDefaults.standardUserDefaults()&lt;br /&gt;  defaults.registerDefaults_({'DisplayAutoLayout': 'NO'})&lt;br /&gt;  app = NSApplication.sharedApplication()&lt;br /&gt;  owner = Owner.alloc().init()&lt;br /&gt;  app.setDelegate_(owner)&lt;br /&gt;  NSBundle.loadGSMarkupNamed_owner_('MainMenu', owner)&lt;br /&gt;  NSApp().run()&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__': main()&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;setup.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;'''&lt;br /&gt;Minimal setup.py example, run with:&lt;br /&gt;% python setup.py py2app&lt;br /&gt;'''&lt;br /&gt;&lt;br /&gt;from distutils.core import setup&lt;br /&gt;import py2app&lt;br /&gt;setup(&lt;br /&gt;  data_files = ['MainMenu.gsmarkup'],&lt;br /&gt;  app = ['browser.py'],&lt;br /&gt;)&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-109997777431240870?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/109997777431240870/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=109997777431240870' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109997777431240870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109997777431240870'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/gmarkup-browser.html' title='GMarkup Browser'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-109980696314614993</id><published>2004-11-07T05:52:00.000Z</published><updated>2004-11-07T05:56:03.146Z</updated><title type='text'>Vancouver XML Users Group</title><content type='html'>I'll be presenting on Renaissance, OS X, and Python at the &lt;a href="http://vanx.org/"&gt;VanX&lt;/a&gt; meeting at 6:30 p.m. on Thursday, December 16th.  By then I should have a lot more programs and utilities to show.  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-109980696314614993?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/109980696314614993/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=109980696314614993' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109980696314614993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109980696314614993'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/vancouver-xml-users-group.html' title='Vancouver XML Users Group'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-109963345105674619</id><published>2004-11-05T05:18:00.000Z</published><updated>2006-05-04T16:17:31.473Z</updated><title type='text'>Small Nerd Example Chapter 2</title><content type='html'>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).&lt;br /&gt;&lt;br /&gt;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 &lt;code&gt;python setup.py py2app --alias&lt;/code&gt; 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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;MainMenu.gsmarkup&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE gsmarkup&amp;gt;&lt;br /&gt;&amp;lt;gsmarkup&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;objects&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;menu type="main"&amp;gt;&lt;br /&gt;    &amp;lt;menu title="ch02" type="apple"&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="About ch02" action="orderFrontStandardAboutPanel:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menu title="Services" type="services"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Hide ch02" action="hide:" key="h"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Hide Others" action="hideOtherApplications:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Show All" action="unhideAllApplications:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Quit ch02" action="terminate:" key="q"/&amp;gt;&lt;br /&gt;    &amp;lt;/menu&amp;gt;&lt;br /&gt;    &amp;lt;menu title="Window" type="windows"&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Minimize Window" &lt;br /&gt;action="performMiniaturize:" key="m"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Bring All to Front" &lt;br /&gt;action="arrangeInFront:"/&amp;gt;&lt;br /&gt;    &amp;lt;/menu&amp;gt;&lt;br /&gt;  &amp;lt;/menu&amp;gt;&lt;br /&gt;  &amp;lt;menu title="Help" type="help"&amp;gt;&lt;br /&gt;    &amp;lt;menuItem title="ch02 Help" keys="?"/&amp;gt;&lt;br /&gt;  &amp;lt;/menu&amp;gt;&lt;br /&gt;&amp;lt;/objects&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/gsmarkup&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;MainWindow.gsmarkup&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE gsmarkup&amp;gt;&lt;br /&gt;&amp;lt;gsmarkup&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;objects&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;window delegate="#NSOwner" resizable="no"&amp;gt;&lt;br /&gt;    &amp;lt;vbox&amp;gt;&lt;br /&gt;      &amp;lt;button target="#NSOwner" action="seed" &lt;br /&gt;  title="Seed random number generator with time"/&amp;gt;&lt;br /&gt;      &amp;lt;button target="#NSOwner" action="generate" &lt;br /&gt;title="Generate random number"/&amp;gt;&lt;br /&gt;      &amp;lt;textField editable="no" id="text" align="center"/&amp;gt;&lt;br /&gt;    &amp;lt;/vbox&amp;gt;&lt;br /&gt;  &amp;lt;/window&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/objects&amp;gt;&lt;br /&gt;&amp;lt;connectors&amp;gt;&lt;br /&gt;  &amp;lt;outlet source="#NSOwner" target="text" key="outputNum"/&amp;gt;&lt;br /&gt;&amp;lt;/connectors&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/gsmarkup&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;ch02.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;'''&lt;br /&gt;  Hillegass Example, Ch. 02&lt;br /&gt;&lt;br /&gt;'''&lt;br /&gt;from Foundation import *&lt;br /&gt;from AppKit import *&lt;br /&gt;from Renaissance import *&lt;br /&gt;import random&lt;br /&gt;&lt;br /&gt;class AppDelegate(NSObject):&lt;br /&gt;  outputNum = None&lt;br /&gt;  def windowWillClose_(self, notification):&lt;br /&gt;    NSApp().terminate_(self)&lt;br /&gt;  def quit_(self, notification):&lt;br /&gt;    NSApp().terminate_(self)&lt;br /&gt;  def close_(self, notification):&lt;br /&gt;    NSApp().terminate_(self)&lt;br /&gt;  def applicationDidFinishLaunching_(self, notification):&lt;br /&gt;    NSBundle.loadGSMarkupNamed_owner_('MainWindow', self)&lt;br /&gt;  def seed(self):&lt;br /&gt;    random.seed()&lt;br /&gt;  def generate(self):&lt;br /&gt;    self.outputNum.setStringValue_(str(random.randint(1,100)))&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;  app = NSApplication.sharedApplication()&lt;br /&gt;  delegate = AppDelegate.alloc().init()&lt;br /&gt;  app.setDelegate_(delegate)&lt;br /&gt;  NSBundle.loadGSMarkupNamed_owner_('MainMenu', delegate)&lt;br /&gt;  NSApp().run()&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__': main()&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;setup.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;'''&lt;br /&gt;Run with:&lt;br /&gt;% python setup.py py2app&lt;br /&gt;or &lt;br /&gt;% python setup.py py2app --alias # while developing&lt;br /&gt;'''&lt;br /&gt;from distutils.core import setup&lt;br /&gt;import py2app&lt;br /&gt;&lt;br /&gt;setup(&lt;br /&gt;  data_files = ['MainMenu.gsmarkup', 'MainWindow.gsmarkup'],&lt;br /&gt;  app = ['ch02.py'],&lt;br /&gt;)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-109963345105674619?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/109963345105674619/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=109963345105674619' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109963345105674619'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109963345105674619'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/small-nerd-example-chapter-2.html' title='Small Nerd Example Chapter 2'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-109963171722035747</id><published>2004-11-05T02:58:00.000Z</published><updated>2004-11-05T05:18:03.513Z</updated><title type='text'>Renaissance Tags</title><content type='html'>Nicola Pera has done a great job building Renaissance, and has worked hard to document it well.  The &lt;a href="http://www.gnustep.it/Renaissance/Manual/index.html"&gt;manual&lt;/a&gt; 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.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;browser/&amp;gt;&lt;br /&gt; titled="yes|no" (default: no)&lt;br /&gt; allowsBranchSelection="yes|no" (default: yes)&lt;br /&gt; allowsEmptySelection="yes|no" (default: no)&lt;br /&gt; allowsMultipleSelection="yes|no" (default: no)&lt;br /&gt; takesTitleFromPreviousColumn="yes|no" (default: yes)&lt;br /&gt; separatesColumns="yes|no" (default: yes)&lt;br /&gt; acceptsArrowKeys="yes|no" (default: yes)&lt;br /&gt; hasHorizontalScrollbars="yes|no" (default: no)&lt;br /&gt; doubleAction="[selector]"&lt;br /&gt; minColumnWidth="[number]"&lt;br /&gt; maxVisibleColumns="[number]"&lt;br /&gt; matrixClass="[subclass of NSMatrix]"&lt;br /&gt;  cellClass="[subclass of NSCell]"&lt;br /&gt;&lt;br /&gt;&amp;lt;colorwell/&amp;gt;&lt;br /&gt; color="[Color]" (See section 2.3.3.6 of the manual for info on Color attributes)&lt;br /&gt;&lt;br /&gt;&amp;lt;form/&amp;gt;&lt;br /&gt;  titleFont="[Font]" (See section 2.3.3.7 of the manual for info on Font attributes)&lt;br /&gt; titleAlignment="left|right|center"&lt;br /&gt;&lt;br /&gt;&amp;lt:label/&amp;gt;&lt;br /&gt; color="[Color]"&lt;br /&gt; backgroundColor="[Color]"&lt;br /&gt; font="[Font]"&lt;br /&gt; align="left|right|center"&lt;br /&gt;&lt;br /&gt;&amp;lt;matrix/&amp;gt; (contains matrixRow elements)&lt;br /&gt; doubleAction="[Selector]"&lt;br /&gt;&lt;br /&gt;&amp;lt;matrixRow/&amp;gt; (contains matrixCell elements)&lt;br /&gt;&lt;br /&gt;&amp;lt;matrixCell/&amp;gt;&lt;br /&gt; title="[String]"&lt;br /&gt; action="[Selector]"&lt;br /&gt; editable="yes|no"&lt;br /&gt; selectable="yes|no"&lt;br /&gt; tag="(???)"  (I'm not sure how tags are used yet)&lt;br /&gt;&lt;br /&gt;&amp;lt;outlineView/&amp;gt;&lt;br /&gt; outlineColumn="[Number]"&lt;br /&gt;&lt;br /&gt;&amp;lt;popupButton/&amp;gt; (contains popupButtonItems)&lt;br /&gt; title="[String]"&lt;br /&gt; pullsDown="yes|no"&lt;br /&gt; autoenabledItems="yes|no" (default: yes)&lt;br /&gt;&lt;br /&gt;&amp;lt;popupButtonItems/&amp;gt;&lt;br /&gt; tag="(???)"&lt;br /&gt; action="[Selector]"&lt;br /&gt; key="(???)"&lt;br /&gt; title="[String]"&lt;br /&gt;&lt;br /&gt;&amp;lt;scrollView/&amp;gt;&lt;br /&gt; hasHorizontalScroller="yes|no"&lt;br /&gt; hasVerticalScroller="yes|no"&lt;br /&gt; borderType="none|line|bezel|groove"&lt;br /&gt;&lt;br /&gt;&amp;lt;secureTextField/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;splitView/&amp;gt;&lt;br /&gt; vertical="yes|no"&lt;br /&gt;&lt;br /&gt;&amp;lt;tableColumn/&amp;gt;&lt;br /&gt; identifier="(???)"&lt;br /&gt; editable="yes|no"&lt;br /&gt; title="[String]"&lt;br /&gt; minWidth="[Number]"&lt;br /&gt; maxWidth="[Number]"&lt;br /&gt; width="[Number]"&lt;br /&gt; resizable="yes|no"&lt;br /&gt;&lt;br /&gt;&amp;lt;tableView/&amp;gt; (contains tableColumns)&lt;br /&gt; dataSource="[Outlet]"&lt;br /&gt; delegate="[Outlet]"&lt;br /&gt; doubleAction="[Selector]"&lt;br /&gt; allowsColumnReordering="yes|no"&lt;br /&gt; allowsColumnResizing="yes|no"&lt;br /&gt; allowsMultipleSelection="yes|no"&lt;br /&gt; allowsEmptySelection="yes|no"&lt;br /&gt; backgroundColor="[Color]"&lt;br /&gt; drawsGrid="yes|no"&lt;br /&gt; gridColor="[Color]"&lt;br /&gt; autosaveName="[String]"&lt;br /&gt;&lt;br /&gt;&amp;lt;textField/&amp;gt;&lt;br /&gt; editable="yes|no"&lt;br /&gt; selectable="yes|no"&lt;br /&gt; align="left|right|center"&lt;br /&gt;&lt;br /&gt;&amp;lt;textView/&amp;gt; &lt;span style="font-weight: bold;"&gt;(Note: Always put a textView inside a scrollView!)&lt;/span&gt;&lt;br /&gt; editable="yes|no"&lt;br /&gt; selectable="yes|no"&lt;br /&gt; richText="yes|no"&lt;br /&gt; usesFontPanel="yes|no"&lt;br /&gt; allowsUndo="yes|no"&lt;br /&gt; usesRuler="yes|no"&lt;br /&gt; importGraphic="yes|no"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Well, that&amp;apos;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-109963171722035747?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/109963171722035747/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=109963171722035747' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109963171722035747'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109963171722035747'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/renaissance-tags.html' title='Renaissance Tags'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-109946460125929567</id><published>2004-11-03T06:36:00.000Z</published><updated>2004-11-03T06:50:01.260Z</updated><title type='text'>Ouch</title><content type='html'>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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-109946460125929567?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/109946460125929567/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=109946460125929567' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109946460125929567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109946460125929567'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/ouch.html' title='Ouch'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-109946227719305453</id><published>2004-11-03T03:52:00.000Z</published><updated>2005-11-07T03:10:23.090Z</updated><title type='text'>Hello Renaissance</title><content type='html'>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 &lt;a href="http://livingcode.blogspot.com/2004/11/new-renaissance.html"&gt;prerequisites&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;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 &lt;em&gt;have&lt;/em&gt; 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 &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;MainMenu.gsmarkup&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE gsmarkup&amp;gt;&lt;br /&gt;&amp;lt;gsmarkup&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;objects&amp;gt;&lt;br /&gt;  &amp;lt;menu type="main"&amp;gt;&lt;br /&gt;    &amp;lt;menu title="Hello World" type="apple"&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="About Hello World" &lt;br /&gt;action="orderFrontStandardAboutPanel:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menu title="Services" type="services"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Hide Hello World" action="hide:" key="h"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Hide Others" action="hideOtherApplications:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Show All" action="unhideAllApplications:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Quit Hello World" action="terminate:" key="q"/&amp;gt;&lt;br /&gt;    &amp;lt;/menu&amp;gt;&lt;br /&gt;    &amp;lt;menu title="Edit"&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Cut" action="cut:" key="x"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Copy" action="copy:" key="c"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Paste" action="paste:" key="v"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Delete" action="delete:"/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Select All" action="selectAll:" key="a"/&amp;gt;&lt;br /&gt;    &amp;lt;/menu&amp;gt;&lt;br /&gt;    &amp;lt;menu title="Window" type="windows"&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Minimize Window" action="performMiniatureize:" &lt;br /&gt;key="m"/&amp;gt;&lt;br /&gt;      &amp;lt;menuSeparator/&amp;gt;&lt;br /&gt;      &amp;lt;menuItem title="Bring All to Front" action="arrangeInFront:"/&amp;gt;&lt;br /&gt;    &amp;lt;/menu&amp;gt;&lt;br /&gt;  &amp;lt;/menu&amp;gt;&lt;br /&gt;&amp;lt;/objects&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/gsmarkup&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;MainWindow.gsmarkup&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE gsmarkup&amp;gt;&lt;br /&gt;&amp;lt;gsmarkup&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;objects&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;window title="Hello World" closable="NO"  &amp;gt;&lt;br /&gt;   &amp;lt;vbox&amp;gt;&lt;br /&gt;    &amp;lt;label&amp;gt;Hello World!&amp;lt;/label&amp;gt;&lt;br /&gt;    &amp;lt;button title="Click this button to quit" action="terminate:"/&amp;gt;&lt;br /&gt;  &amp;lt;/vbox&amp;gt;&lt;br /&gt;&amp;lt;/window&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/objects&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/gsmarkup&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;hello.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;from Foundation import *&lt;br /&gt;from AppKit import *&lt;br /&gt;from Renaissance import *&lt;br /&gt;&lt;br /&gt;class MyApplicationDelegate(NSObject):&lt;br /&gt;  def cut_(self, sender):&lt;br /&gt;    pass&lt;br /&gt;  def applicationDidFinishLaunching_(self, notification):&lt;br /&gt;    NSBundle.loadGSMarkupNamed_owner_('MainWindow', self)&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;  app = NSApplication.sharedApplication()&lt;br /&gt;  delegate = MyApplicationDelegate.alloc().init()&lt;br /&gt;  app.setDelegate_(delegate)&lt;br /&gt;  NSBundle.loadGSMarkupNamed_owner_('MainMenu', delegate)&lt;br /&gt;  NSApp().run()&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__': main()&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;setup.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;'''&lt;br /&gt;Minimal setup.py example, run with:&lt;br /&gt;% python setup.py py2app&lt;br /&gt;'''&lt;br /&gt;&lt;br /&gt;from distutils.core import setup&lt;br /&gt;import py2app&lt;br /&gt;setup(&lt;br /&gt;  data_files = ['MainMenu.gsmarkup', 'MainWindow.gsmarkup'],&lt;br /&gt;  app = ['hello.py'],&lt;br /&gt;)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Commentary&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-109946227719305453?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/109946227719305453/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=109946227719305453' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109946227719305453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109946227719305453'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/hello-renaissance.html' title='Hello Renaissance'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-109945338076770527</id><published>2004-11-03T02:54:00.000Z</published><updated>2004-11-03T03:48:14.006Z</updated><title type='text'>A New Renaissance</title><content type='html'>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&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Declarative&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Simple *and* powerful&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Able to replace NIB files and Interface Builder&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Text-based, not binary &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Agile for rapid development, adapts to rapidly changing code&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Able to specify simple apps completely, in code, without resorting to pictures or talking the user through mouse gestures&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;Prerequisites (this is my setup, others may work, but I haven't tested them).&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;A Mac&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;OS X (10.3)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;PyObjC (1.1), available from &lt;a href="http://pyobjc.sourceforge.net/"&gt;http://pyobjc.sourceforge.net/&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;py2app (0.1.4), available from &lt;a href="http://pythonmac.org/wiki/py2app"&gt;http://pythonmac.org/wiki/py2app&lt;/a&gt; (we'll use this to build our double-clickable applications)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Renaissance framework (0.8), available from &lt;a href="http://www.gnustep.it/Renaissance/Download.html"&gt;http://www.gnustep.it/Renaissance/Download.html&lt;/a&gt; (this is the secret sauce)&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Once you have the prerequisites installed, you need to make Renaissance available from Python.  In your site-packages directory (on my machine this is &lt;em&gt;/System/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/site-packages&lt;/em&gt;) add a Renaissance directory containing the following file:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;__init__.py&lt;/strong&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;import objc, AppKit, Foundation&lt;br /&gt;objc.loadBundle('Renaissance', globals(), &lt;br /&gt;  bundle_path='/Library/Frameworks/Renaissance.framework')&lt;br /&gt;del objc, AppKit, Foundation&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Well, that was easy enough.  Next up, a Hello World application.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-109945338076770527?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/109945338076770527/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=109945338076770527' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109945338076770527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109945338076770527'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/new-renaissance.html' title='A New Renaissance'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-109937324264244354</id><published>2004-11-02T05:27:00.000Z</published><updated>2004-11-02T05:39:59.490Z</updated><title type='text'>Losing my Nibs</title><content type='html'>I've been working with &lt;a href="#pygame"&gt;PyGame&lt;/a&gt; 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.&lt;br /&gt;&lt;br /&gt;So lately I've been turning my attention more and more toward &lt;a href="#pyobjc"&gt;PyObjC&lt;/a&gt;.  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 &lt;a href="#appkido"&gt;AppKiDo&lt;/a&gt;, 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.&lt;br /&gt;&lt;br /&gt;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 &lt;a href="#macpythonmail"&gt;mailing list&lt;/a&gt; and &lt;a href="#macpythonwiki"&gt;wiki&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;And all this leads to examples like &lt;a href="http://developer.apple.com/documentation/Cocoa/Conceptual/TextArchitecture/Tasks/TextEditor.html"&gt;Build a text editor in 15 minutes&lt;/a&gt; or &lt;a href="http://66.102.7.104/search?q=cache:6-vXeR_P108J:crankycoder.com/archives/000052.html+Victor+Ng+Cocoa+NSDocument&amp;hl=en"&gt;An example using NSDocument&lt;/a&gt; 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.&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;* Declarative&lt;br /&gt;* Simple *and* powerful&lt;br /&gt;* Able to replace NIB files and Interface Builder&lt;br /&gt;* Text-based, not binary&lt;br /&gt;* Agile for rapid development, adapts to rapidly changing code&lt;br /&gt;* Able to specify simple apps completely, in code, without resorting to pictures or talking the user through mouse gestures&lt;br /&gt;&lt;br /&gt;And the best part is that I think I've found it.  More in my next post.&lt;br /&gt;&lt;br /&gt;&lt;a name="pygame"&gt;PyGame&lt;/a&gt;: &lt;a href="http://pygame.org/"&gt;http://pygame.org/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name="pyobjc"&gt;PyObjC&lt;/a&gt;: &lt;a href="http://pyobjc.sourceforge.net/"&gt;http://pyobjc.sourceforge.net/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name="macpythonmail"&gt;MacPython Mailing Lis&lt;/a&gt;t: &lt;a href="http://mail.python.org/mailman/listinfo/pythonmac-sig"&gt;http://mail.python.org/mailman/listinfo/pythonmac-sig&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name="macpythonwiki"&gt;MacPython Wiki&lt;/a&gt;: &lt;a href="http://www.pythonmac.org/wiki/FrontPage"&gt;http://www.pythonmac.org/wiki/FrontPage&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name="appkido"&gt;AppKiDo&lt;/a&gt;: &lt;a href="http://homepage.mac.com/aglee/downloads/"&gt;http://homepage.mac.com/aglee/downloads/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-109937324264244354?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/109937324264244354/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=109937324264244354' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109937324264244354'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109937324264244354'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/11/losing-my-nibs.html' title='Losing my Nibs'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-109539573337311964</id><published>2004-09-17T04:29:00.000Z</published><updated>2004-09-17T04:51:27.380Z</updated><title type='text'>DOM back to XML in Python</title><content type='html'>OK, time to crank up to speed, it's been a lot longer than I intended between posts.&lt;br /&gt;&lt;br /&gt;In the last episode we learned how to initialize many of the DOMs and DOM-like tools for Python from an XML document.  Today we're going to see how to convert these back to XML from the DOM.  So fasten your seatbelts and let's go.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;br /&gt;import domParse&lt;br /&gt;from xml.dom.ext.c14n import Canonicalize&lt;br /&gt;&lt;br /&gt;def stringMinidom(filename):&lt;br /&gt;    return Canonicalize(domParse.parseMinidom(filename))&lt;br /&gt;&lt;br /&gt;def string4Dom(filename):&lt;br /&gt;    return Canonicalize(domParse.parse4Dom(filename))&lt;br /&gt;&lt;br /&gt;def stringDomlette(filename):&lt;br /&gt;    return Canonicalize(domParse.parseDomlette(filename))&lt;br /&gt;&lt;br /&gt;def stringLibXml(filename):&lt;br /&gt;    # pretty-printed, which may not be what you want, &lt;br /&gt;    # depending on the XML in question&lt;br /&gt;    return domParse.parseLibXml(filename).serialize(encoding='utf-8', &lt;br /&gt;        format=True)&lt;br /&gt;    # 4DOM c14n breaks because libXML doesn't give you a DOM&lt;br /&gt;    # return Canonicalize(domParse.parseLibXml(filename))&lt;br /&gt;&lt;br /&gt;def stringPxDom(filename):&lt;br /&gt;    import pxdom&lt;br /&gt;    serializer = pxdom.LSSerializer()&lt;br /&gt;    return serializer.writeToString(domParse.parsePxDom(filename))&lt;br /&gt;    #return Canonicalize(domParse.parsePxDom(filename))&lt;br /&gt;&lt;br /&gt;def main(filename):&lt;br /&gt;    print '4DOM:', string4Dom(filename)&lt;br /&gt;    print 'Domlette:', stringDomlette(filename)&lt;br /&gt;    print 'MiniDom:', stringMinidom(filename)&lt;br /&gt;    print 'LibXml:', stringLibXml(filename)&lt;br /&gt;    print 'PxDom:', stringPxDom(filename)&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__': main(domParse.small_filename)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;As you can see, there's not much to it.  This codes does require that you've installed the PyXML package, but if you're serious about XML in Python, that will already be the case.  In our next outing we can explore some of the less DOM-like, but more Pythonic ways to play with XML.&lt;br /&gt;&lt;br /&gt;PyXML: &lt;a href="http://pyxml.sourceforge.net/"&gt;http://pyxml.sourceforge.net/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;4Suite: &lt;a href="http://4suite.org/index.xhtml"&gt;http://4suite.org/index.xhtml&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;libxml: &lt;a href="http://www.xmlsoft.org/"&gt;http://www.xmlsoft.org/&lt;/a&gt; (instructions for the python bindings are linked from this page)&lt;br /&gt;&lt;br /&gt;pxdom: &lt;a href="http://www.doxdesk.com/software/py/pxdom.html"&gt;http://www.doxdesk.com/software/py/pxdom.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;You may now return your trays to their upright positions.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-109539573337311964?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/109539573337311964/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=109539573337311964' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109539573337311964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/109539573337311964'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/09/dom-back-to-xml-in-python.html' title='DOM back to XML in Python'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-108796196519595315</id><published>2004-06-23T03:31:00.000Z</published><updated>2004-09-15T19:32:50.106Z</updated><title type='text'>Initializing a DOM in Python</title><content type='html'>There are many DOM options in Python, and I have trouble remembering how to load a document into the various DOMs. Here are a few common ones, although there are many variations on them (loading from URL or string, different configurations, etc.). This should provide a starting point.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;# Examples for reading in various DOMs from an XML file&lt;br /&gt;&lt;br /&gt;# MiniDOM&lt;br /&gt;def parseMinidom(filename):&lt;br /&gt;    try:&lt;br /&gt;        from xml.dom.minidom import parse&lt;br /&gt;        doc = parse(filename)&lt;br /&gt;        return doc&lt;br /&gt;    except Exception, e:&lt;br /&gt;        return 'parseMinidom() failed with exception %s' % e&lt;br /&gt;&lt;br /&gt;# 4DOM&lt;br /&gt;def parse4Dom(filename):&lt;br /&gt;    try:&lt;br /&gt;        from xml.dom.ext.reader.Sax2 import Reader&lt;br /&gt;        f = file(filename)&lt;br /&gt;        reader = Reader(validate=0,keepAllWs=0,catName=None)&lt;br /&gt;        doc = reader.fromStream(f)  # slow!&lt;br /&gt;        f.close()&lt;br /&gt;        return doc&lt;br /&gt;    except Exception, e:&lt;br /&gt;    return 'parse4Dom() failed with exception %s' % e&lt;br /&gt;&lt;br /&gt;# Domlette&lt;br /&gt;def parseDomlette(filename):&lt;br /&gt;    try:&lt;br /&gt;        from Ft.Xml.Domlette import NonvalidatingReader as reader&lt;br /&gt;        f = file(filename)&lt;br /&gt;        uri = 'file:///%s' % filename # suppress warning&lt;br /&gt;        doc = reader.parseStream(f, uri)&lt;br /&gt;        f.close()&lt;br /&gt;        return doc&lt;br /&gt;    except Exception, e:&lt;br /&gt;        return 'parseDomlette() failed with exception %s' % e&lt;br /&gt;&lt;br /&gt;# libXml&lt;br /&gt;def parseLibXml(filename):&lt;br /&gt;    try:&lt;br /&gt;        import libxml2&lt;br /&gt;        f = file(filename)&lt;br /&gt;        data = f.read()&lt;br /&gt;        f.close()&lt;br /&gt;        doc = libxml2.parseDoc(data)&lt;br /&gt;        return doc&lt;br /&gt;    except Exception, e:&lt;br /&gt;        return 'parseLibXml() failed with exception %s' % e&lt;br /&gt;&lt;br /&gt;# pxDom&lt;br /&gt;def parsePxDom(filename):&lt;br /&gt;    try:&lt;br /&gt;        import pxdom&lt;br /&gt;        doc = pxdom.parse(filename)&lt;br /&gt;        return doc&lt;br /&gt;    except Exception, e:&lt;br /&gt;        return 'parsePxDom() failed with exception %s' % e&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    import sys&lt;br /&gt;    filename = sys.argv[1]&lt;br /&gt;    print '4DOM:', parse4Dom(filename)&lt;br /&gt;    print 'Domlette:', parseDomlette(filename)&lt;br /&gt;    print 'MiniDom:', parseMinidom(filename)&lt;br /&gt;    print 'LibXml:', parseLibXml(filename)&lt;br /&gt;    print 'PxDom:', parsePxDom(filename)&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__': main()&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-108796196519595315?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/108796196519595315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=108796196519595315' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/108796196519595315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/108796196519595315'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/06/initializing-dom-in-python.html' title='Initializing a DOM in Python'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7108966.post-108554993980569471</id><published>2004-05-26T05:34:00.000Z</published><updated>2005-11-19T17:00:48.876Z</updated><title type='text'>Testing, testing, is this thing on?</title><content type='html'>OK, time to check out the new Blogger.  I'm looking for a home to move to from ManilaSites, which was fun while it lasted, but is too slow to use now.  I'm also going to try to refocus on what I started out to do with the original &lt;a href="http://livingcode.manilasites.com/"&gt;Living Code&lt;/a&gt; site, which is to write about programming for the fun of it, mostly in Python, but also some Javascript, some dialects of XML, maybe even some more arcane things.  I may still descend into political diatribes and bad jokes, but if the blogger thing works out I'm going to try to keep to the code.&lt;br /&gt;&lt;br /&gt;I wonder how to get syntax highlighting in Blogger?&lt;br /&gt;&lt;br /&gt;And I don't see any options for categories or blogrolling.  Gonna have to roll up my sleeves and start exploring.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7108966-108554993980569471?l=livingcode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://livingcode.blogspot.com/feeds/108554993980569471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7108966&amp;postID=108554993980569471' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/108554993980569471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7108966/posts/default/108554993980569471'/><link rel='alternate' type='text/html' href='http://livingcode.blogspot.com/2004/05/testing-testing-is-this-thing-on.html' title='Testing, testing, is this thing on?'/><author><name>Dethe Elza</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ki__LELGCb4/SrgbPvA7x-I/AAAAAAAAAA8/wzR_e7tDzwM/S220/Dethe+bald+avatar.png'/></author><thr:total>0</thr:total></entry></feed>
