Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 1 | <HTML><HEAD><TITLE>Using python to create Macintosh applications, part one</TITLE></HEAD> |
| 2 | <BODY> |
| 3 | <H1>Using python to create Macintosh applications, part one</H1> |
| 4 | <HR> |
| 5 | |
| 6 | This document will show you how to create a simple mac-style |
| 7 | application using Python. We will glance at how to use dialogs and |
| 8 | resources. <p> |
| 9 | |
| 10 | The example application we look at will be a simple program with a |
| 11 | dialog that allows you to control and monitor InterSLIP, a device |
| 12 | driver that connects your mac to the Internet via a modem connection. |
| 13 | <A HREF="example1/InterslipControl-1.py">Source</A> and resource file |
| 14 | (in binary and <A |
| 15 | HREF="example1/InterslipControl-1.rsrc.hqx">BinHex</A> form for |
| 16 | downloading) for this application are available in the <A |
| 17 | HREF="example1">example1</A> folder (which you will have to download |
| 18 | if you are reading this document over the net and if you want to look |
| 19 | at the resources). <p> |
| 20 | |
| 21 | We will use a C extension module module "interslip" that allows a |
| 22 | Python program to control and monitor the behaviour of the low-level |
| 23 | driver, and we will create the user interface around that. If you want |
| 24 | to actually run the code, you will obvously need InterSLIP and the |
| 25 | interslip module. The latter is available as a dynamically loadable |
Jack Jansen | a2139fe | 1998-02-25 15:40:35 +0000 | [diff] [blame^] | 26 | extension for PowerPC/cfm68k Pythons, and may be compiled in your static 68K |
| 27 | Python. As of this writing there is still a slight |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 28 | problem with the Python interslip module causing it to say "file not |
| 29 | found" if the driver is not loaded yet. The workaround is to load the |
| 30 | driver by starting InterSLIP Control and quitting it. <p> |
| 31 | |
| 32 | <CITE> |
| 33 | If you are interested in building your own extensions to python you |
| 34 | should check out the companion document <A |
| 35 | HREF="plugins.html">Creating Macintosh Python C extensions</A>, |
| 36 | which tells you how to build your own C extension. Not completely |
| 37 | coincidental this document uses the interslip module that we will use |
| 38 | here as an example. <p> |
| 39 | </CITE> |
| 40 | |
| 41 | <H2><A NAME="dialog-resources">Creating dialog resources</A></H2> |
| 42 | |
| 43 | Let us start with the creative bit: building the dialogs and creating |
| 44 | an icon for our program. For this you need ResEdit, and a reasonable |
| 45 | working knowledge of how to use it. "Inside Mac" or various books on |
| 46 | macintosh programming will help here. <p> |
| 47 | |
| 48 | There is one fine point that deserves to be mentioned here: <A |
| 49 | NAME="resource-numbering">resource numbering</A>. Because often your |
| 50 | resources will be combined with those that the Python interpreter and |
| 51 | various standard modules need you should give your DLOG and DITL |
| 52 | resources numbers above 512. 128 and below are reserved for Apple, |
Jack Jansen | a2139fe | 1998-02-25 15:40:35 +0000 | [diff] [blame^] | 53 | 128-228 are for extensions like Tk, |
| 54 | 228-255 for the Python interpreter and 256-511 for standard |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 55 | modules. If you are writing a module that you will be distributing for |
| 56 | inclusion in other people's programs you may want to register a number |
| 57 | in the 256-511 range, contact Guido or myself or whoever you think is |
| 58 | "in charge" of Python for the Macintosh at the moment. Even though the |
| 59 | application we are writing at the moment will keep its resources in a |
| 60 | separate resource file it is still a good idea to make sure that no |
| 61 | conflicts arise: once you have opened your resource file any attempt |
| 62 | by the interpreter to open a dialog will also search your resource |
| 63 | file. <p> |
| 64 | |
| 65 | Okay, let's have a look at InterslipControl-1.rsrc, our resource file. |
| 66 | The DLOG and accompanying DITL resource both have number 512. Since |
| 67 | ResEdit creates both with default ID=128 you should take care to |
| 68 | change the number on both. The dialog itself is pretty basic: four |
| 69 | buttons (connect, disconnect, update status and quit), two labels and |
| 70 | two status fields. <p> |
| 71 | |
| 72 | <H2><A NAME="modal-dialog">An application with a modal dialog</A></H2> |
| 73 | |
| 74 | Next, we will have to write the actual application. For this example, |
| 75 | we will use a modal dialog. This means that we will put up the dialog |
| 76 | and go into a loop asking the dialog manager for events (buttons |
| 77 | pushed). We handle the actions requested by the user until the quit |
| 78 | button is pressed, upon which we exit our loop (and the program). This |
| 79 | way of structuring your program is actually rather antisocial, since |
| 80 | you force the user to do whatever you, the application writer, happen |
| 81 | to want. A modal dialog leaves no way of escape whatsoever (except |
| 82 | command-option-escape), and is usually not a good way to structure |
| 83 | anything but the most simple questions. Even then: how often have you |
| 84 | been confronted with a dialog asking a question that you could not |
| 85 | answer because the data you needed was obscured by the dialog itself? |
| 86 | In the next example we will look at an application that does pretty |
| 87 | much the same as this one but in a more user-friendly way. <p> |
| 88 | |
| 89 | On to the code itself, in file <A |
| 90 | HREF="example1/InterslipControl-1.py"> InterslipControl-1.py</A>. Have |
| 91 | a copy handy before you read on. The file starts off with a |
| 92 | textstring giving a short description. Not many tools do anything with |
| 93 | this as yet, but at some point in the future we <EM>will</EM> have all |
| 94 | sorts of nifty class browser that will display this string, so just |
| 95 | include it. Just put a short description at the start of each module, |
| 96 | class, method and function. After the initial description and some |
| 97 | comments, we import the modules we need. <p> |
| 98 | |
| 99 | <A NAME="easydialogs"><CODE>EasyDialogs</CODE></A> is a handy standard |
| 100 | module that provides you with routines that put up common text-only |
| 101 | modal dialogs: |
| 102 | <UL> |
| 103 | <LI> <CODE>Message(str)</CODE> |
| 104 | displays the message "str" and an OK button, |
| 105 | <LI> <CODE>AskString(prompt, default)</CODE> |
| 106 | asks for a string, displays OK and Cancel buttons, |
| 107 | <LI> <CODE>AskYesNoCancel(question, default)</CODE> |
| 108 | displays a question and Yes, No and Cancel buttons. |
| 109 | </UL> |
| 110 | |
| 111 | <A NAME="res"><CODE>Res</CODE></A> is a pretty complete interface to |
| 112 | the MacOS Resource Manager, described fully in Inside Mac. There is |
| 113 | currently no documentation of it, but the Apple documentation (or |
| 114 | Think Ref) will help you on your way if you remember two points: |
| 115 | <UL> |
| 116 | <LI> Resources are implemented as Python objects, and each routine |
| 117 | with a resource first argument is implemented as a python method. |
| 118 | <LI> When in doubt about the arguments examine the routines docstring, |
| 119 | as in <CODE>print Res.OpenResFile.__doc__</CODE> |
| 120 | </UL> |
| 121 | |
| 122 | Similarly, <A NAME="dlg"><CODE>Dlg</CODE></A> is an interface to the |
| 123 | Dialog manager (with Dialogs being implemented as python objects and |
| 124 | routines with Dialog arguments being methods). The sys module you |
| 125 | know, I hope. <A NAME="interslip"><CODE>Interslip</CODE></A>, |
| 126 | finally, is the module with the interface to the InterSLIP driver. We |
| 127 | use four calls from it: |
| 128 | <UL> |
| 129 | <LI> <CODE>open()</CODE> |
| 130 | opens the driver |
| 131 | <LI> <CODE>connect()</CODE> |
| 132 | asks it to initiate a connection procedure (without waiting) |
| 133 | <LI> <CODE>disconnect()</CODE> |
| 134 | asks it to initiate a disconnection procedure (without waiting) |
| 135 | <LI> <CODE>status()</CODE> |
| 136 | returns the current connection status in the form of an integer state, |
| 137 | an integer "message sequence number" and a message string. |
| 138 | </UL> |
| 139 | |
| 140 | Next in the source file we get definitions for our dialog resource |
| 141 | number and for the item numbers in our dialog. These should match the |
| 142 | situation in our resource file InterslipControl-1.rsrc, |
| 143 | obviously. Then we get an array converting numeric state codes |
| 144 | returned by <CODE>interslip.status()</CODE> to textual messages. <p> |
| 145 | |
| 146 | On to the main program. We start off with opening our resource file, |
| 147 | which should live in the same folder as the python source. If we |
| 148 | cannot open it we use <CODE>EasyDialogs</CODE> to print a message and |
| 149 | exit. You can try it: just move the resource file somewhere else for a |
| 150 | moment. Then, we try to open the interslip driver, again catching an |
| 151 | error. All modules that raise <A NAME="macos-errors">MacOS error |
| 152 | exceptions</A> will pass a 2-tuple to the exception handler with the |
| 153 | first item being the numeric <CODE>OSErr</CODE> code and the second |
| 154 | one being an informative message. If no informative message is |
| 155 | available it will be the rather uninformative <CODE>"MacOS Error |
| 156 | -12345"</CODE>, but at least the second item will always be a |
| 157 | printable string. Finally we call do_dialog() to do the real work. <p> |
| 158 | |
| 159 | <CODE>Do_dialog()</CODE> uses <CODE>Dlg.GetNewDialog()</CODE> to open |
| 160 | a dialog window initialized from 'DLOG' resource ID_MAIN and putting |
| 161 | it on screen in the frontmost position. Next, we go into a loop, |
| 162 | calling <CODE>Dlg.ModalDialog()</CODE> to wait for the next user |
| 163 | action. <CODE>ModalDialog()</CODE> will return us the item number that |
| 164 | the user has clicked on (or otherwise activated). It will handle a few |
| 165 | slightly more complicated things also, like the user typing into |
| 166 | simple textfields, but it will <EM>not</EM> do things like updating |
| 167 | the physical appearance of radio buttons, etc. See Inside Mac or |
| 168 | another programming guide for how to handle this |
| 169 | yourself. Fortunately, our simple application doesn't have to bother |
| 170 | with this, since buttons are the only active elements we have. So, we |
| 171 | do a simple switch on item number and call the appropriate routine to |
| 172 | implement the action requested. Upon the user pressing "quit" we |
| 173 | simply leave the loop and, hence, <CODE>do_dialog()</CODE>. This will |
| 174 | cause the python dialog object <CODE>my_dlg</CODE> to be deleted and |
| 175 | the on-screen dialog to disappear. <p> |
| 176 | |
| 177 | <A NAME="dialog-warning">Time for a warning</A>: be very careful what |
| 178 | you do as long as a dialog is on-screen. Printing something, for |
| 179 | instance, may suddenly cause the standard output window to appear over |
| 180 | the dialog, and since we took no measures to redraw the dialog it will |
| 181 | become very difficult to get out of the dialog. Also, command-period |
| 182 | may or may not work in this situation. I have also seen crashes in |
| 183 | such a situation, probably due to the multiple event loops involved or |
| 184 | some oversight in the interpreter. You have been warned. <p> |
| 185 | |
| 186 | The implementation of the "update status" command can use a bit more |
| 187 | explaining: we get the new information with <CODE>do_status()</CODE> |
| 188 | but now we have to update the on-screen dialog to present this |
| 189 | information to the user. The <CODE>GetDialogItem()</CODE> method of |
| 190 | the dialog returns three bits of information about the given item: its |
| 191 | type, its data handle and its rect (the on-screen <CODE>x,y,w,h</CODE> |
| 192 | coordinates). We are only interested in the data handle here, on which |
| 193 | we call <CODE>SetDialogItemText()</CODE> to set our new text. Note |
| 194 | here that python programmers need not bother with the C-string versus |
| 195 | pascal-string controversy: the python glue module knows what is needed |
| 196 | and converts the python string to the correct type. <p> |
| 197 | |
| 198 | Finally, the three implementation routines <CODE>do_connect()</CODE>, |
| 199 | <CODE>do_disconnect()</CODE> and <CODE>do_status()</CODE> are simply |
| 200 | boring wrappers around the corresponding interslip methods that will |
| 201 | put up a dialog in case of an error. <p> |
| 202 | |
| 203 | And that concludes our first example of the use of resources and |
| 204 | dialogs. Next, you could have a look at the source of EasyDialogs for |
| 205 | some examples of using input fields and filterprocs. Or, go on with |
| 206 | reading the <A HREF="example2.html">second part</A> of this document |
| 207 | to see how to implement a better version of this application. Not only |
| 208 | will it allow the user to go back to the finder (or other apps) when |
| 209 | your application is running, it will also free her of the RSI-inducing |
| 210 | chore of pressing "update status" continuously... <p> |
| 211 | |
| 212 | |