[Pythonmac-SIG] Nib-less program success

Dethe Elza delza at livingcode.org
Sat Oct 30 22:23:14 CEST 2004


Hi folks,

I just wanted to share some recent successes I've had with PyObjC.   
First, Bob, I've been using py2app and it's working wonderfully.  I'm  
very happy with the improvements you've made over the old bundlebuilder  
method.

I've also decided it was time to take a look at Renaissance more  
closely.  I really like the way it's designed, much more like HTML than  
most XML UI resource tools, which tend to be more like Java with XML  
syntax.  So far I have only scratched the surface, but I'm already  
impressed.

I was not able to find any examples of using Renaissance from PyObjC  
without using Nibs as well.  One of the (at least 4) forks of DrawBot  
uses Renaissance to build UI widgets and sheets, but it still uses Nibs  
for the main application.  I was trying to get Renaissance working  
without Nibs and crashing Python, about to give up when I tried one  
more thing and it worked, so I thought I'd share.

First, here is the main menu file, which can be re-used as the basis  
for most applications, and is mostly copied directly from the  
Renaissance CurrencyConverter example.

============ begin file MainMenu.gsmarkup ===================

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

============ end file MainMenu.gsmarkup ===================

Next is the small test window, drawn directly from Renaissance  
documentation:

============ begin file MainWindow.gsmarkup ===================

<?xml version="1.0"?>
<!DOCTYPE gsmarkup>
<gsmarkup>
<objects>
   <window title="Button Test" closable="NO" frameAutosaveName="main" >
     <button title="Click this button to quit" action="terminate:"/>
   </window>
</objects>
</gsmarkup>

============ end file MainWindow.gsmarkup ===================

And here is the Python code to run it.  The cut: command is implemented  
as a no-op to demonstrate how the menu commands automatically become  
enabled when their controller implements the selector specified in the  
gsmarkup file.  The two gsmarkup files are loaded in separate areas of  
the code to demonstrate how easy it is to load from markup.

============ begin file RenTest.py ===================

from Foundation import *
from AppKit import *
from Renaissance import *
from PyObjCTools.AppHelper import runEventLoop

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

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

if __name__ == '__main__': main()

============ end file RenTest.py ===================

Putting it all together is done using py2app:

============ begin file setup.py ===================

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

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

============ end file setup.py ===================

Finally, after putting the Renaissance.framework in /Library/Frameworks  
I've also put the following in  
/System/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/ 
site-packages/Renaissance/ to enable Renaissance globally on my system.

============ begin file __init__.py ===================

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

============ end file __init__.py ===================

OK, that's it.  About 79 lines of code to have a simple app with menus  
and a window and basic Mac app behaviour (hide, minimize, Cmd-H, Cmd-Q,  
etc.).

Why do I care about avoiding Nibs?  Two main reasons.  Interface  
Builder is great as far as it goes, but it has a steep learning curve  
to use it well.  And when I'm developing an application and want to  
refactor it, or rapidly iterate through several tests, I have trouble  
finding everywhere in Interface Builder that a certain outlet or  
selector is used, to change the target or the selector.  And because it  
stores serialized objects, it doesn't pick up changes to my code unless  
I force it to.  I'm afraid I prefer a format I can grep to a visual one  
(IB for Renaissance is in progress I understand, which would be great  
-- I'm not constitutionally opposed to graphic layout tools, I just  
don't like them being enforced).  So using Renaissance addresses my  
problems in using IB.

The second reason is that I see Cocoa examples on the web (either ObjC  
or PyObjC) which go like this:  "Look, I can make an [editor,  
web-browser, etc.] with only 5 lines of code!" followed by 25 screen  
shots of IB with paragraphs of instructions for wiring up the widgets  
and outlets to the 5 lines of code.  This seems disingenuous to me, and  
it's fragile because the examples which worked with the last version of  
IB don't necessarily reflect the current (or next) version.  Maybe it's  
just me, but I have a lot easier time following the 79 lines of code  
above versus the 5 lines + screen shots + talking you through the  
screen shots.

Anyway, I found this very exciting and hope I haven't bored y'all too  
much.  I'm loving PyObjC even more than usual now, which is something.   
Now to write something more substantial...

--Dethe

Excel is the Awk of Windows --Joel Spolsky



More information about the Pythonmac-SIG mailing list