[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