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