1=============================================================
2Tutorial - Adding Python code to an existing ObjC application
3=============================================================
4
5In this tutorial we are going to take an existing ObjC application and
6add Python and PyObjC to it.  One of the reasons why you may want to do
7this is because some things are much simpler in Python than in ObjC, mainly
8due to the rich library Python has.
9
10To follow the tutorial you need:
11
12 * PyObjC 1.3.1
13 * py2app 0.2 or later (included in the binary installer for PyObjC)
14 * Python 2.3 or later (note: PyObjC is NOT compatible with MacPython-OS9)
15 * Mac OS X 10.3 or later
16 * Xcode Tools
17
18If you do not have a ``/Developer`` folder, then you do not have Xcode Tools
19installed.  See `Getting the Xcode Tools`_.
20
21.. _`Getting the Xcode Tools`: http://developer.apple.com/tools/download/
22
23The application we are going to modify is Apple's SimpleComboBox example.
24This example shows you how to use combo boxes, but that is not what interests
25us right now: the application pretends to be a database application that allows
26you to keep notes (such as track list) for your CD collection.  With such an
27application it feels silly that even though you want to type notes on
28the CD you are currently playing in iTunes you still have to retype
29album title, artist and genre.  This is what we are going to fix: we
30are going to add a button "ask iTunes", which will use Python's
31AppleScript support to ask iTunes about the currently playing track
32and fill in the fields for you.  
33
34Follow these steps:
35
361. Make a copy of ``/Developer/Examples/AppKit/SimpleComboBox`` to work on.
37   Let's call this ``SimpleComboBoxPlus``:
38
39   .. sourcecode: sh
40   
41      $ cp -R /Developer/Examples/AppKit/SimpleComboBox SimpleComboBoxPlus
42
43  From this point on, all shell commands take place from this
44  ``SimpleComboBoxPlus`` folder.
45    
462. Open it in Xcode, build it, and see what it does.
47
483. Open ``CDInfoDocument.nib``.  Select the Class View, ``NSObject``, subclass
49   as ``ITunesCommunication``.  Give the class an ``askITunes:`` action.
50   Instantiate the class as object ``ITunesCommunication``.  This wll be the
51   class that we write in Python.
52   
534. Go to the object view again, open the Window.
54
555. Move the text box down a bit to make space, add a button "ask iTunes".
56
576. Connect this button to the ``askITunes:`` action of the
58   ``ITunesCommunication`` object.
59    
607. We now need to write the code implementing the ``ITunesCommunication``
61    class.  As this tutorial is about using PyObjC in existing ObjC programs
62    and not about PyObjC itself, we are going to skip writing the code and
63    simply copy ``ITunesCommunication_1.py`` to ``ITunesCommunication.py``.
64
658. Now we need to create the build script for our plugin, create a file named
66   ``setup.py`` with the following contents:
67
68
69   .. sourcecode:: python
70
71        from distutils.core import setup
72        import py2app
73
74        setup(
75            plugin = ['ITunesCommunication.py']
76        )   
77
78   You may also copy this file from ``setup.py``. 
79
809. Run the setup script to create a temporary plugin bundle for development:
81
82   .. sourcecode: sh
83
84        $ python setup.py py2app -A
85
86   Note that we use the ``-A`` argument to create an alias plugin bundle at
87   ``dist/ITunesCommunication.py``.  Alias bundles contain an alias to the
88   main script (``ITunesCommunication.py``) and symlinks to the data files
89   (none in this case).  This allows us to keep working on the source files
90   without having to rebuild the application.  This alias bundle is similar
91   to a ZeroLink executable in Xcode - it is for DEVELOPMENT ONLY, and will
92   not work on other machines.
93
9410. Add ``dist/ITunesCommunication.plugin`` to the Resources folder in your
95    Xcode project.  You can do this by ctrl-clicking the Resources folder
96    and choosing "Add Existing Files...".  Make sure to choose
97    "Create Folder References for any added folders".
98
9911. Open ``main.m``, it is in the "Other Sources" folder in your Xcode
100    project, and change the main(...) function to the following:
101
102    .. sourcecode:: objective-c
103
104        int main(int argc, const char *argv[]) {
105            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
106            NSString *pluginPath = [[NSBundle mainBundle]
107                                        pathForResource:@"ITunesCommunication"
108                                                 ofType:@"plugin"];
109            NSBundle *pluginBundle = [NSBundle bundleWithPath:pluginPath];
110            [pluginBundle load];
111            [pool release];
112            return NSApplicationMain(argc, argv);
113        }
114
115
116    You may also copy a full main.m from ``main.m``.  This code ensures
117    that our ITunesCommunication plugin is loaded before the nib
118    files.
119
12012. Build and run.  When you press the "Ask iTunes" the "CD Title" and
121    "Band Name" fields will be filled with one of the best albums of the last
122    few years :-)
123    
12413. Now we need to make the program talk to iTunes.  The current MacPython
125    interface to the Open Scripting Architecture requires an extra step when
126    compared to AppleScript: you need to manually generate a Python package
127    that wraps all the AppleScript terminology for an application.  To make
128    matters more complicated iTunes is one of those special cases where the
129    standard way to generate this package (start the application, ask it for
130    its terminology) does not work, so we have to actually look into the
131    bowels of ``iTunes.app``.  This leads to the following hefty command line
132    which you should run in the ``SimpleComboBoxPlus`` directory:
133
134    .. sourcecode:: sh
135    
136        $ cd SimpleComboBoxPlus
137        $ pythonw -c "from gensuitemodule import main;main()" \
138            --output iTunes --creator hook --resource \
139            /Applications/iTunes.app/Contents/Resources/iTunes.rsrc
140    
14114. Finally, add the code to ``ITunesCommunication.py`` to actually communicate
142    with iTunes.  We cop out and copy it from ``ITunesCommunication_2.py``.
143    
14415. Build and run.  If you press the button when iTunes is playing the Title
145    and Band names will be filled, otherwise they will be cleared.  In a real
146    application you would disable the "Ask iTunes" button unless iTunes was
147    active.  All that is left as an exercise to the reader.
148
14916. To make this application redistributable, perform the following commands
150    to make the plugin redistributable:
151
152    .. sourcecode:: sh
153
154        $ rm -rf dist
155        $ python setup.py py2app
156
157    Then, from Xcode, clean your project (shift-cmd-K), switch to Deployment
158    mode, and rebuild.
159 
160A minor variation
161-----------------
162
163There a several projects that improve upon the built-in AppleScript support
164(or to be more precise "application scripting support").  One of those is
165`AppScript`_.
166
167.. _`AppScript`: http://freespace.virgin.net/hamish.sanderson/appscript.html
168
169When you have this module installed you can replace the contents of
170``ITunesCommuncation.py`` with ``ITunesCommunication_AppScript.py``,
171and you can skip step 13 entirely.
172