1=======================================
2Creating your first PyObjC application.
3=======================================
4
5WARNING: This tutorial assumes you're using Xcode 2.5 and is therefore not 
6entirely valid with Xcode 3 (that is MacOS 10.5).
7
8In this tutorial you will learn how to create your first Python Cocoa
9application: a simple dialog that allows you to convert amounts of money from
10one currency to another.  Definitely easier to do with a calculator, but in the
11process of following the tutorial you will learn which bits of Apple's Cocoa
12documentation apply to PyObjC and which bits are different, and how to adapt
13the different bits to PyObjC from Objective-C.
14
15To follow the tutorial you need:
16
17 * PyObjC 1.3.1
18 * py2app 0.2 or later (included in the binary installer for PyObjC)
19 * Python 2.3 or later (note: PyObjC is NOT compatible with MacPython-OS9)
20 * Mac OS X 10.2 or later
21 * Xcode Tools (was Developer Tools for Mac OS X 10.2)
22
23If you do not have a ``/Developer`` folder, then you do not have Xcode Tools
24installed.  See `Getting the Xcode Tools`_.
25
26.. _`Getting the Xcode Tools`: http://developer.apple.com/tools/download/
27
28Getting Started
29---------------
30
311. Create a work directory ``src``.  Check which Python you have installed
32   PyObjC for, by running ``python`` and checking that ``import Foundation``
33   works.  If it does not work it could be that you have installed PyObjC for
34   ``/usr/local/bin/python`` but Apple's ``/usr/bin/python`` comes first in
35   your ``$PATH``.  Make sure you use the right python whereever it says
36   ``python`` in this tutorial.
37   
382. Start Interface Builder, select *Cocoa Application*
39   in the new file dialog, save this file as ``src/MainMenu.nib``.
40   
413. Proceed with the instructions as lined out in Apple's
42   `Developing Cocoa Objective-C Applications: a Tutorial`_, `chapter 3`_,
43   just after the section "*Creating the Currency Converter Interface*".
44   Work through "Defining the Classes of Currency Converter", "Connecting
45   ConverterController to the Interface", and stop at
46   "*Implementing the Classes of Currency Converter*", as we are going to do
47   this in Python, not Objective-C.  Your nib file should now be the same as
48   step3-MainMenu.nib_.
49   
50.. _`Developing Cocoa Objective-C Applications: a Tutorial`: http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCTutorial/index.html
51.. _`chapter 3`: http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCTutorial/index.html?http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCTutorial/chapter03/chapter_3_section_1.html
52.. _step3-MainMenu.nib: step3-MainMenu.nib.zip
53   
544. Create the skeleton Python script by running the ``nibclassbuilder`` script.
55   ``nibclassbuilder`` will parse the NIB file and create a skeleton module for
56   you.  Invoke it as follows (from the ``src`` directory):
57
58   .. sourcecode:: sh
59   
60       $ python -c "import PyObjCScripts.nibclassbuilder" MainMenu.nib > CurrencyConverter.py
61               
62   Depending on your installation, the ``nibclassbuilder`` script may be on your ``$PATH``.
63   If so, it can be invoked as such:
64
65   .. sourcecode:: sh
66
67       $ nibclassbuilder MainMenu.nib > CurrencyConverter.py
68   
69   The result of this can be seen in step4-CurrencyConverter.py_.
70
71.. _step4-CurrencyConverter.py: step4-CurrencyConverter.py.html
72               
73Testing the user interface
74--------------------------
75
765. Now we need to create an build script for CurrencyConverter.  To do this,
77   create a file named ``setup.py`` with the following contents:
78
79   .. sourcecode:: python
80      :linenos:
81   
82        from distutils.core import setup
83        import py2app
84
85        setup(
86            app=['CurrencyConverter.py'],
87            data_files=['MainMenu.nib'],
88        )
89
90   The result of this can be seen in step5-setup.py_.
91
92.. _step5-setup.py: step5-setup.py.html
93   
946. Run the setup script to create a temporary application bundle for
95   development:
96
97   .. sourcecode:: sh
98
99        $ python setup.py py2app -A
100      
101   Note that we use the ``-A`` argument to create an alias bundle at 
102   ``dist/CurrencyConverter.app``.  Alias bundles contain an alias to the
103   main script (``CurrencyConverter.py``) and symlinks to the data files
104   (``MainMenu.nib``), rather than including them and their dependencies
105   into a standalone application bundle.  This allows us to keep working on
106   the source files without having to rebuild the application.  This alias
107   bundle is similar to a ZeroLink executable for Xcode - it is for
108   DEVELOPMENT ONLY, and will not work on other machines.
109   
1107. Run the program.  This can be done in three ways:
111
112   - double-click ``dist/CurrencyConverter`` from the Finder
113     (you won't see the .app extension)
114
115   - open it from the terminal with:
116
117     .. sourcecode:: sh
118   
119        $ open dist/CurrencyConverter.app
120       
121   - run it directly from the Terminal, as::
122
123     .. sourcecode:: sh
124   
125        $ ./dist/CurrencyConverter.app/Contents/MacOS/CurrencyConverter
126       
127   The last method is typically the best to use for development: it leaves
128   stdout and stderr connected to your terminal session so you can see what
129   is going on if there are errors, and it allows you to interact with ``pdb``
130   if you are using it to debug your application.  Note that your application
131   will likely appear in the background, so you will have to cmd-tab or click
132   on its dock icon to see its user interface.
133   
134   The other methods cause stdout and stderr to go to the Console log, which
135   can be viewed with ``/Applications/Utilities/Console.app``.
136   
137   When you run your script as it is now it should behave identically as when
138   you tested your interface in Interface Builder in step 3, only now the
139   skeleton is in Python, not Objective-C.
140   
141
142Writing the code
143----------------
144
1458.  Time to actually write some code.  Open ``CurrencyConverter.py`` in your
146    favorite text editor.  Follow Apple's documentation again, chapter 3,
147    section "Implementing Currency Converter's Classes".  To translate this
148    Objective C code to Python syntax, we will need to do some name mangling of
149    the selectors.  See *An introduction to PyObjC* for the details, but the
150    short is that:
151
152    .. sourcecode:: objective-c
153
154        [anObject modifyArg: arg1 andAnother: arg2]
155
156   translates into the following Python code, by replacing the colons in the
157   selector with underscores, and passing the arguments as you would with a
158   normal Python method call:
159
160   .. sourcecode:: python
161
162        anObject.modifyArg_andAnother_(arg1, arg2)
163   
164   Note that we don't do this mangling for ``Converter.convertAmount()``: this
165   method is only called by other Python code, so there is no need to go
166   through the name mangling.  Also, if we would want to make this method
167   callable from ObjC code we may have to tell the PyObjC runtime system about
168   the types of the arguments, so it could do the conversion.  This is beyond
169   the scope of this first tutorial, *An introduction to PyObjC* has a little
170   more detail on this.
171   
172   The application should now be fully functional, try it.  The results of what
173   we have up to now can be seen in step8-CurrencyConverter.py_.
174   
175.. _step8-CurrencyConverter.py: step8-CurrencyConverter.py.html
176   
177Extending the functionality
178---------------------------
179
1809.  We are going to add one more goodie, just to show how you edit an existing
181    application.  The main problem, which may be obvious, is that we cannot run
182    ``nibclassbuilder`` again because we would destroy all the code we wrote in
183    steps 5 and 8, so we do this by hand.  What we are going to do is add an
184    "invert rate" command, because I always get this wrong: instead of typing
185    in the exchange rate from dollars to euros I type in the rate to convert
186    from euros to dollars.
187   
188    Open ``MainMenu.nib`` in Interface Builder.  Select the *Classes* view and
189    then select the ``ConverterController`` class.  In the info panel select
190    the *Attributes* from the popup.  Select the *Actions* tab, and add an
191    action ``invertRate:``.  You have now told Interface Builder that instances
192    of the ``ConverterController`` class have grown a new method
193    ``invertRate_()``.
194   
195    In the ``MainMenu.nib main`` window open the *MainMenu* menubar.  Select
196    the ``Edit`` menu.  Make sure the *Menus* palette is open and selected,
197    drag a separator to the ``Edit`` menu and then drag an ``Item`` there.
198    Double-click the item and set the text to ``Invert Exchange Rate``.
199   
200    Make the connection by control-dragging from the new
201    ``Invert Exchange Rate`` menu item to the ``ConverterController`` instance
202    in the Instances tab in the ``MainMenu.nib`` main window.
203
204    *NOTE:* you drag to the *instance* of ``ConverterController``, not to the
205    class.
206
207    In the *Info* panel, *Connections* section, select ``invertRate:`` and
208    press *Connect*. 
209   
21010. We know our program can't invert rates yet, because we haven't actually
211    written the code to do it, but we are going to try it anyway, just to see
212    what sort of spectacular crash we get.  Alas, nothing spectacular about it:
213    when the NIB is loaded the Cocoa runtime system tries to make the
214    connection, notices that we have no ``invertRate_()`` method in our
215    ``ConverterController`` class and it gives an error message:
216
217    .. sourcecode:: sh
218       
219       $ ./dist/CurrencyConverter.app/Contents/MacOS/CurrencyConverter 
220       2004-12-09 03:29:09.957 CurrencyConverter[4454] Could not connect the action 
221       invertRate: to target of class ConverterController
222   
223    Moreover, it has disabled the ``Invert Exchange Rate`` menu command and
224    continues, so the program works as it did before, only with one more
225    (disabled) menu item.
226   
227Debugging
228---------
229
23011. Writing the code is easy: add a method ``invertRate_(self, sender)`` that
231    gets the float value of ``rateField``, inverts it and puts it back.  We
232    deliberately forget to test for divide by zero.  We run the program again,
233    and now the menu entry is enabled.  After trying it with a couple of
234    non-zero exchange rates we try it with an exchange rate of zero (or empty,
235    which is the same).  We get a dialog box giving the Python exception, and
236    offering the choice of continuing or quitting. 
237    
238    To debug this application with pdb, start the application with the
239    following command line:
240
241    .. sourcecode:: sh
242
243        $ env USE_PDB=1 ./dist/CurrencyConverter.app/Contents/MacOS/CurrencyConverter
244
245    When running in this mode, we will get a ``pdb.post_mortem(...)`` console
246    in the terminal instead of the alert panel.  You can see this in action if
247    you try and invert an exchange rate of ``0``.
248      
24912. Fix the final bug by testing for ``rate == 0.0`` in ``invertRate_()``.
250    The result is in the step12-src_ directory.
251    
252.. _step12-src: step12-src.zip
253   
254Creating a redistributable application
255--------------------------------------
256
257Your application is finished, and you want to run it on other computers, or
258simply just move it to the ``Applications`` folder (or anywhere else) and
259insulate it from the original source code.
260
261This can be done with the following steps from the ``src`` directory:
262
263 .. sourcecode: sh
264
265    $ rm -rf dist
266    $ python setup.py py2app
267
268Now the application bundle located at ``dist/CurrencyConverter.app`` is a fully
269standalone application that should run on any computer running the same major
270version of Mac OS X or later.  This means that applications built on
271Mac OS X 10.2 are compatible with Mac OS X 10.3, but NOT vice versa.  If you
272are not using an Apple-supplied version of Python, a subset of your Python
273installation will be included in this application.
274
275For more complicated examples of py2app usage to do things such as change the
276application's icon, see the Examples or the py2app documentation.
277