to the Python Lib directory::
$ setenv PYLIB /Library/Frameworks/Python.framework/Versions/Current/lib/python2.3
or if you use bash as your shell::
$ export PYLIB=/Library/Frameworks/Python.framework/Versions/Current/lib/python2.3
*XXXX Editing Note:* This step is different if you use PyObjC on top of Apple's Python 2.2.
Moreover, the bundlebuilder step is going to be different too.
in the new file dialog, save this file as ``src/MainMenu.nib``.
"*Developing Cocoa Objective-C Applications: a Tutorial*", chapter 3,
just after the section "*Open the Main Nib File*". Stop when you get
to the section "*Implementing the Classes of Currency Converter*",
as we are going to do this in Python, not Objective-C. Your nib file
should now be the same as step3-MainMenu.nib_
When invoked as a main program from the command line ``NibClassBuilder`` will
parse the NIB file and create a skeleton module for you. Invoke
it as follows (from the ``src`` directory)::
$ python $PYLIB/site-packages/PyObjC/AppKit/NibClassBuilder.py \
MainMenu.nib > CurrencyConverter.py
The result of this can be seen in step4-CurrencyConverter.py_
in the future this will become simpler, but for now we need to add a main program
that jumps through various hoops to get the application started.
The results of this can be seen in step5-CurrencyConverter.py_.
*XXXX Implementation Note:* NibClassBuilder should do this for you, at least
in the future it could well be that this step is not needed during development,
or that it becomes simpler, but for now we do the following::
$ python $PYLIB/plat-mac/bundlebuilder.py --link --nib=MainMenu \\
--mainprogram=CurrencyConverter.py --resource=MainMenu.nib build
There are a few things to note:
- We use the ``--link`` argument. This creates a ``.app`` bundle which has symlinks
to our source files (``CurrencyConverter.py`` and ``MainMenu.nib``) in stead of copies.
This allows us to keep working on the sources without having to re-run bundlebuilder
- You have to specify MainMenu twice: once (with ``--resource``) to get it linked/copied
into the bundle and once (with ``--nib``) to get it listed in the ``.plist`` file.
- double-click ``build/CurrencyConverter.app`` from the Finder (where you won't see the
- similarly, open it from the terminal with::
$ open build/CurrencyConverter.app
- run it directly from the Terminal, as::
The last method is actually the best to use: it leaves stdout and stderr connected
to your terminal session so you can see what is going on if there are errors. When
running with the other two methods stdout and stderr go to the console.
When you run your script as it is now it should behave identically as when you
tested your interface in Interface Builder in step 3, only now the skeleton is
in Python, not Objective-C.
some. Follow Apple's documentation again, chapter 3, section "Implementing
Currency Converter's Classes". We need to do some name mangling on ObjC
names to get the corresponding Python names, see *XXXX Editing Note: need reference*
for the full details, but
in short if the ObjC name of a method is ``modifyArg:andAnother:``, in
other words, if an ObjC call would be::
[object modifyArg: arg1 andAnother: arg2]
the Python name will be ``modifyArg_andAnother_`` and you invoke it as::
Note that we don't do this mangling for ``Converter.ConvertAmount()``: this method is
only called by other Python code, so there is no need to go through the name mangling.
Also, if we would want to make this method callable from ObjC code we would have
to tell the PyObjC runtime system about the types of the arguments, so it could
do the conversion. This is beyond the scope of this first tutorial, see
*XXXX Editing Note: need reference* for more information.
The application should now be fully functional, try it. The results of what we have
up to now can be seen in step8-CurrencyConverter.py_.
The main problem, which may be obvious, is that we cannot run NibClassBuilder again
because we would destroy all the code we wrote in steps 5 and 8, so we do this by
What we are going to do is add an "invert rate" command, because I always get this
wrong: in stead of typing in the exchange rate from dollars to euros I type in the
rate to convert from euros to dollars.
Open ``MainMenu.nib`` in Interface Builder. Select the *Classes* view and there select the
``ConverterController`` class. In the info panel select the *Attributes* from the popup.
Select the *Actions* tab, and add an action ``invertRate:``. You have now told Interface Builder
that instances of the ``ConverterController`` class have grown a new method ``invertRate_()``.
In the ``MainMenu.nib main`` window open the *MainMenu* menubar. Select the ``Edit``
menu. Make sure the *Menus* palette is open and selected, drag a separator to the
``Edit`` menu and then drag an ``Item`` there. Double click the item and set the text to
``Invert Exchange Rate``.
Make the connection by control-dragging from the new ``Invert Exchange Rate`` menu item to
the ``ConverterController`` instance in the Instances tab in the ``MainMenu.nib`` main window.
*NOTE:* you drag to the *instance* of ``ConverterController``, not to the class. This is logical
if you think about it, but I keep forgetting it myself all the time too.
In the *Info* panel, *Connections* section, select ``invertRate:`` and press *Connect*.
*NOTE:* that is another thing I always forget: pressing *Connect* after selecting the action:-)
to do it, but we are going to try it anyway, just to see what sort of spectacular
crash we get. Alas, nothing spectacular about it: when the NIB is loaded the Cocoa runtime
system tries to make the conection, notices that we have no ``invertRate_()`` method in
our ``ConverterController`` class and it gives an error message::
2003-03-24 16:22:43.037 CurrencyConverter Could not connect the action
invertRate: to target of class ConverterController
Moreover, it has disabled the ``Invert Exchange Rate`` menu command and continues, so the
program really works as it did before, only with one more (disabled) menu item.
value of ``rateField``, inverts it and puts it back. We deliberately forget to test for
divide by zero. We run the program again, and now the menu entry is enabled. After
trying it with a couple of non-zero exchange rates we try it with an exchange rate of
zero (or empty, which is the same). We get a dialog box giving the Python exception, and
offering the choice of continuing or quitting. If we select *Quit* then we get a normal
Python exception traceback in the Terminal window. The exception is actually re-raised,
so we can use the standard Python trick to debug this: set shell variable
``PYTHONINSPECT`` to ``1``, run our program, try to invert an exchange rate of ``0``, press quit.
At the ``>>>`` prompt, type ``import pdb ; pdb.pm()`` and we can inspect all local variables,