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