| 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> | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 3 |  | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 4 | <H1>Using python to create Macintosh applications, part one</H1> | 
 | 5 | <HR> | 
 | 6 |  | 
 | 7 | This document will show you how to create a simple mac-style | 
 | 8 | application using Python. We will glance at how to use dialogs and | 
 | 9 | resources. <p> | 
 | 10 |  | 
 | 11 | The example application we look at will be a simple program with a | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 12 | dialog that allows you to perform domain name lookups on IP addresses | 
 | 13 | and hostnames. | 
 | 14 | The <A HREF="example1/dnslookup-1.py">source code</A> and  | 
 | 15 | <A HREF="example1/dnslookup-1.rsrc">resource file</A>  | 
 | 16 | for this application are available in the <A | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 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 |  | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 21 | We will use the builtin module "socket" that allows a | 
 | 22 | Python program to perform all sorts of networking functions, and we  | 
 | 23 | will create the user interface around that. You should be able to run | 
 | 24 | the sample code with the standard Python distribution.<p> | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 25 |  | 
 | 26 | <CITE> | 
 | 27 | If you are interested in building your own extensions to python you | 
 | 28 | should check out the companion document <A | 
 | 29 | HREF="plugins.html">Creating Macintosh Python C extensions</A>, | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 30 | which tells you how to build your own C extension. | 
 | 31 | <p> | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 32 | </CITE> | 
 | 33 |  | 
 | 34 | <H2><A NAME="dialog-resources">Creating dialog resources</A></H2> | 
 | 35 |  | 
 | 36 | Let us start with the creative bit: building the dialogs and creating | 
 | 37 | an icon for our program. For this you need ResEdit, and a reasonable | 
 | 38 | working knowledge of how to use it. "Inside Mac" or various books on | 
 | 39 | macintosh programming will help here. <p> | 
 | 40 |  | 
 | 41 | There is one fine point that deserves to be mentioned here: <A | 
 | 42 | NAME="resource-numbering">resource numbering</A>.  Because often your | 
 | 43 | resources will be combined with those that the Python interpreter and | 
 | 44 | various standard modules need you should give your DLOG and DITL | 
 | 45 | resources numbers above 512. 128 and below are reserved for Apple, | 
| Jack Jansen | a2139fe | 1998-02-25 15:40:35 +0000 | [diff] [blame] | 46 | 128-228 are for extensions like Tk, | 
 | 47 | 228-255 for the Python interpreter and 256-511 for standard | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 48 | modules. If you are writing a module that you will be distributing for | 
 | 49 | inclusion in other people's programs you may want to register a number | 
 | 50 | in the 256-511 range, contact Guido or myself or whoever you think is | 
 | 51 | "in charge" of Python for the Macintosh at the moment. Even though the | 
 | 52 | application we are writing at the moment will keep its resources in a | 
 | 53 | separate resource file it is still a good idea to make sure that no | 
 | 54 | conflicts arise: once you have opened your resource file any attempt | 
 | 55 | by the interpreter to open a dialog will also search your resource | 
 | 56 | file. <p> | 
 | 57 |  | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 58 | Okay, let's have a look at dnslookup-1.rsrc, our resource file. | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 59 | The DLOG and accompanying DITL resource both have number 512. Since | 
 | 60 | ResEdit creates both with default ID=128 you should take care to | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 61 | change the number on both. The dialog itself is pretty basic: two | 
 | 62 | buttons (Lookup and Quit), two labels and | 
 | 63 | two text entry areas, one of which is used for output only.  Here's what | 
 | 64 | the dialog will look like at run time<p> | 
 | 65 | <div align=center> | 
 | 66 | <img width=324 height=189 src="example1/dnslookup-1.gif" alt="dialog image"> | 
 | 67 | </div> | 
 | 68 | <p> | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 69 |  | 
 | 70 | <H2><A NAME="modal-dialog">An application with a modal dialog</A></H2> | 
 | 71 |  | 
 | 72 | Next, we will have to write the actual application. For this example, | 
 | 73 | we will use a modal dialog. This means that we will put up the dialog | 
 | 74 | and go into a loop asking the dialog manager for events (buttons | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 75 | pushed). We handle the actions requested by the user until the Quit | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 76 | button is pressed, upon which we exit our loop (and the program). This | 
 | 77 | way of structuring your program is actually rather antisocial, since | 
 | 78 | you force the user to do whatever you, the application writer, happen | 
 | 79 | to want. A modal dialog leaves no way of escape whatsoever (except | 
 | 80 | command-option-escape), and is usually not a good way to structure | 
 | 81 | anything but the most simple questions.  Even then: how often have you | 
 | 82 | been confronted with a dialog asking a question that you could not | 
 | 83 | answer because the data you needed was obscured by the dialog itself? | 
 | 84 | In the next example we will look at an application that does pretty | 
 | 85 | much the same as this one but in a more user-friendly way. <p> | 
 | 86 |  | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 87 | The code itself is contained in the file <A | 
 | 88 | HREF="example1/dnslookup-1.py"> dnslookup-1.py</A>. Have | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 89 | a copy handy before you read on.  The file starts off with a | 
 | 90 | textstring giving a short description. Not many tools do anything with | 
 | 91 | this as yet, but at some point in the future we <EM>will</EM> have all | 
 | 92 | sorts of nifty class browser that will display this string, so just | 
 | 93 | include it. Just put a short description at the start of each module, | 
 | 94 | class, method and function.  After the initial description and some | 
 | 95 | comments, we import the modules we need. <p> | 
 | 96 |   | 
 | 97 | <A NAME="easydialogs"><CODE>EasyDialogs</CODE></A> is a handy standard | 
 | 98 | module that provides you with routines that put up common text-only | 
 | 99 | modal dialogs: | 
 | 100 | <UL> | 
 | 101 | <LI> <CODE>Message(str)</CODE> | 
 | 102 | displays the message "str" and an OK button, | 
 | 103 | <LI> <CODE>AskString(prompt, default)</CODE> | 
 | 104 | asks for a string, displays OK and Cancel buttons, | 
 | 105 | <LI> <CODE>AskYesNoCancel(question, default)</CODE> | 
 | 106 | displays a question and Yes, No and Cancel buttons. | 
 | 107 | </UL> | 
 | 108 |  | 
 | 109 | <A NAME="res"><CODE>Res</CODE></A> is a pretty complete interface to | 
 | 110 | the MacOS Resource Manager, described fully in Inside Mac. There is | 
 | 111 | currently no documentation of it, but the Apple documentation (or | 
 | 112 | Think Ref) will help you on your way if you remember two points: | 
 | 113 | <UL> | 
 | 114 | <LI> Resources are implemented as Python objects, and each routine | 
 | 115 | with a resource first argument is implemented as a python method. | 
 | 116 | <LI> When in doubt about the arguments examine the routines docstring, | 
 | 117 | as in <CODE>print Res.OpenResFile.__doc__</CODE> | 
 | 118 | </UL> | 
 | 119 |   	 | 
 | 120 | Similarly, <A NAME="dlg"><CODE>Dlg</CODE></A> is an interface to the | 
 | 121 | Dialog manager (with Dialogs being implemented as python objects and | 
 | 122 | routines with Dialog arguments being methods). The sys module you | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 123 | know, I hope.  The string module is an often used module that enables | 
 | 124 | you to perform many string related operations.  In this case however, we | 
 | 125 | are only using the "digits" constant from the string module.  We could | 
 | 126 | have simply defined "digits" as "0123456789".  The socket module enables  | 
 | 127 | us to perform the domain name lookups. We | 
 | 128 | use two calls from it: | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 129 | <UL> | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 130 | <LI> <CODE>gethostbyaddr()</CODE> | 
 | 131 | returns the hostname associated with an IP address | 
 | 132 | <LI> <CODE>gethostbyname()</CODE> | 
 | 133 | returns the IP address associated with a hostname | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 134 | </UL> | 
 | 135 |    | 
 | 136 | Next in the source file we get definitions for our dialog resource | 
 | 137 | number and for the item numbers in our dialog. These should match the | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 138 | situation in our resource file dnslookup-1.rsrc, | 
 | 139 | obviously.<p> | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 140 |  | 
 | 141 | On to the main program. We start off with opening our resource file, | 
 | 142 | which should live in the same folder as the python source. If we | 
 | 143 | cannot open it we use <CODE>EasyDialogs</CODE> to print a message and | 
 | 144 | exit. You can try it: just move the resource file somewhere else for a | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 145 | moment. Then we call do_dialog() to do the real work. <p> | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 146 |  | 
 | 147 | <CODE>Do_dialog()</CODE> uses <CODE>Dlg.GetNewDialog()</CODE> to open | 
 | 148 | a dialog window initialized from 'DLOG' resource ID_MAIN and putting | 
 | 149 | it on screen in the frontmost position.  Next, we go into a loop, | 
 | 150 | calling <CODE>Dlg.ModalDialog()</CODE> to wait for the next user | 
 | 151 | action. <CODE>ModalDialog()</CODE> will return us the item number that | 
 | 152 | the user has clicked on (or otherwise activated). It will handle a few | 
 | 153 | slightly more complicated things also, like the user typing into | 
 | 154 | simple textfields, but it will <EM>not</EM> do things like updating | 
 | 155 | the physical appearance of radio buttons, etc. See Inside Mac or | 
 | 156 | another programming guide for how to handle this | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 157 | yourself. Fortunately, our simple application doesn't have to bother with this, | 
 | 158 | since buttons and textfields are the only active elements we have. So, we do a | 
 | 159 | simple switch on item number and call the appropriate routine to implement the | 
 | 160 | action requested. Upon the user pressing "Quit" we simply leave the loop and, | 
 | 161 | hence, <CODE>do_dialog()</CODE>. This will cause the python dialog object | 
 | 162 | <CODE>my_dlg</CODE> to be deleted and the on-screen dialog to disappear. <p> | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 163 |  | 
 | 164 | <A NAME="dialog-warning">Time for a warning</A>: be very careful what | 
 | 165 | you do as long as a dialog is on-screen.  Printing something, for | 
 | 166 | instance, may suddenly cause the standard output window to appear over | 
 | 167 | the dialog, and since we took no measures to redraw the dialog it will | 
 | 168 | become very difficult to get out of the dialog. Also, command-period | 
 | 169 | may or may not work in this situation. I have also seen crashes in | 
 | 170 | such a situation, probably due to the multiple event loops involved or | 
 | 171 | some oversight in the interpreter. You have been warned. <p> | 
 | 172 |  | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 173 | The implementation of the "Lookup" command can use a bit more | 
 | 174 | explaining: we get the necessary information with <CODE>dnslookup()</CODE> | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 175 | but now we have to update the on-screen dialog to present this | 
 | 176 | information to the user. The <CODE>GetDialogItem()</CODE> method of | 
 | 177 | the dialog returns three bits of information about the given item: its | 
 | 178 | type, its data handle and its rect (the on-screen <CODE>x,y,w,h</CODE> | 
 | 179 | coordinates). We are only interested in the data handle here, on which | 
 | 180 | we call <CODE>SetDialogItemText()</CODE> to set our new text.  Note | 
 | 181 | here that python programmers need not bother with the C-string versus | 
 | 182 | pascal-string controversy: the python glue module knows what is needed | 
 | 183 | and converts the python string to the correct type. <p> | 
 | 184 |  | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 185 | And that concludes our first example of the use of resources and | 
 | 186 | dialogs. Next, you could have a look at the source of EasyDialogs for | 
 | 187 | some examples of using input fields and filterprocs. Or, go on with | 
 | 188 | reading the <A HREF="example2.html">second part</A> of this document | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 189 | to see how to implement a better version of this application.<p> | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 190 |  | 
| Jack Jansen | 2450a25 | 1999-12-03 15:15:28 +0000 | [diff] [blame] | 191 | </BODY> | 
 | 192 | </HTML> | 
| Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 193 |  |