| 
									
										
										
										
											1996-03-18 13:38:52 +00:00
										 |  |  | <HTML><HEAD><TITLE>Using python to create Macintosh applications, part one</TITLE></HEAD> | 
					
						
							|  |  |  | <BODY> | 
					
						
							|  |  |  | <H1>Using python to create Macintosh applications, part one</H1> | 
					
						
							|  |  |  | <HR> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This document will show you how to create a simple mac-style | 
					
						
							|  |  |  | application using Python. We will glance at how to use dialogs and | 
					
						
							|  |  |  | resources. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The example application we look at will be a simple program with a | 
					
						
							|  |  |  | dialog that allows you to control and monitor InterSLIP, a device | 
					
						
							|  |  |  | driver that connects your mac to the Internet via a modem connection. | 
					
						
							|  |  |  | <A HREF="example1/InterslipControl-1.py">Source</A> and resource file | 
					
						
							|  |  |  | (in binary and <A | 
					
						
							|  |  |  | HREF="example1/InterslipControl-1.rsrc.hqx">BinHex</A> form for | 
					
						
							|  |  |  | downloading) for this application are available in the <A | 
					
						
							|  |  |  | HREF="example1">example1</A> folder (which you will have to download | 
					
						
							|  |  |  | if you are reading this document over the net and if you want to look | 
					
						
							|  |  |  | at the resources). <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | We will use a C extension module module "interslip" that allows a | 
					
						
							|  |  |  | Python program to control and monitor the behaviour of the low-level | 
					
						
							|  |  |  | driver, and we will create the user interface around that. If you want | 
					
						
							|  |  |  | to actually run the code, you will obvously need InterSLIP and the | 
					
						
							|  |  |  | interslip module. The latter is available as a dynamically loadable | 
					
						
							| 
									
										
										
										
											1998-02-25 15:40:35 +00:00
										 |  |  | extension for PowerPC/cfm68k Pythons, and may be compiled in your static 68K | 
					
						
							|  |  |  | Python. As of this writing there is still a slight | 
					
						
							| 
									
										
										
										
											1996-03-18 13:38:52 +00:00
										 |  |  | problem with the Python interslip module causing it to say "file not | 
					
						
							|  |  |  | found" if the driver is not loaded yet. The workaround is to load the | 
					
						
							|  |  |  | driver by starting InterSLIP Control and quitting it. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <CITE> | 
					
						
							|  |  |  | If you are interested in building your own extensions to python you | 
					
						
							|  |  |  | should check out the companion document <A | 
					
						
							|  |  |  | HREF="plugins.html">Creating Macintosh Python C extensions</A>, | 
					
						
							|  |  |  | which tells you how to build your own C extension. Not completely | 
					
						
							|  |  |  | coincidental this document uses the interslip module that we will use | 
					
						
							|  |  |  | here as an example. <p> | 
					
						
							|  |  |  | </CITE> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <H2><A NAME="dialog-resources">Creating dialog resources</A></H2> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Let us start with the creative bit: building the dialogs and creating | 
					
						
							|  |  |  | an icon for our program. For this you need ResEdit, and a reasonable | 
					
						
							|  |  |  | working knowledge of how to use it. "Inside Mac" or various books on | 
					
						
							|  |  |  | macintosh programming will help here. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | There is one fine point that deserves to be mentioned here: <A | 
					
						
							|  |  |  | NAME="resource-numbering">resource numbering</A>.  Because often your | 
					
						
							|  |  |  | resources will be combined with those that the Python interpreter and | 
					
						
							|  |  |  | various standard modules need you should give your DLOG and DITL | 
					
						
							|  |  |  | resources numbers above 512. 128 and below are reserved for Apple, | 
					
						
							| 
									
										
										
										
											1998-02-25 15:40:35 +00:00
										 |  |  | 128-228 are for extensions like Tk, | 
					
						
							|  |  |  | 228-255 for the Python interpreter and 256-511 for standard | 
					
						
							| 
									
										
										
										
											1996-03-18 13:38:52 +00:00
										 |  |  | modules. If you are writing a module that you will be distributing for | 
					
						
							|  |  |  | inclusion in other people's programs you may want to register a number | 
					
						
							|  |  |  | in the 256-511 range, contact Guido or myself or whoever you think is | 
					
						
							|  |  |  | "in charge" of Python for the Macintosh at the moment. Even though the | 
					
						
							|  |  |  | application we are writing at the moment will keep its resources in a | 
					
						
							|  |  |  | separate resource file it is still a good idea to make sure that no | 
					
						
							|  |  |  | conflicts arise: once you have opened your resource file any attempt | 
					
						
							|  |  |  | by the interpreter to open a dialog will also search your resource | 
					
						
							|  |  |  | file. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Okay, let's have a look at InterslipControl-1.rsrc, our resource file. | 
					
						
							|  |  |  | The DLOG and accompanying DITL resource both have number 512. Since | 
					
						
							|  |  |  | ResEdit creates both with default ID=128 you should take care to | 
					
						
							|  |  |  | change the number on both. The dialog itself is pretty basic: four | 
					
						
							|  |  |  | buttons (connect, disconnect, update status and quit), two labels and | 
					
						
							|  |  |  | two status fields. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <H2><A NAME="modal-dialog">An application with a modal dialog</A></H2> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Next, we will have to write the actual application. For this example, | 
					
						
							|  |  |  | we will use a modal dialog. This means that we will put up the dialog | 
					
						
							|  |  |  | and go into a loop asking the dialog manager for events (buttons | 
					
						
							|  |  |  | pushed). We handle the actions requested by the user until the quit | 
					
						
							|  |  |  | button is pressed, upon which we exit our loop (and the program). This | 
					
						
							|  |  |  | way of structuring your program is actually rather antisocial, since | 
					
						
							|  |  |  | you force the user to do whatever you, the application writer, happen | 
					
						
							|  |  |  | to want. A modal dialog leaves no way of escape whatsoever (except | 
					
						
							|  |  |  | command-option-escape), and is usually not a good way to structure | 
					
						
							|  |  |  | anything but the most simple questions.  Even then: how often have you | 
					
						
							|  |  |  | been confronted with a dialog asking a question that you could not | 
					
						
							|  |  |  | answer because the data you needed was obscured by the dialog itself? | 
					
						
							|  |  |  | In the next example we will look at an application that does pretty | 
					
						
							|  |  |  | much the same as this one but in a more user-friendly way. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | On to the code itself, in file <A | 
					
						
							|  |  |  | HREF="example1/InterslipControl-1.py"> InterslipControl-1.py</A>. Have | 
					
						
							|  |  |  | a copy handy before you read on.  The file starts off with a | 
					
						
							|  |  |  | textstring giving a short description. Not many tools do anything with | 
					
						
							|  |  |  | this as yet, but at some point in the future we <EM>will</EM> have all | 
					
						
							|  |  |  | sorts of nifty class browser that will display this string, so just | 
					
						
							|  |  |  | include it. Just put a short description at the start of each module, | 
					
						
							|  |  |  | class, method and function.  After the initial description and some | 
					
						
							|  |  |  | comments, we import the modules we need. <p> | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  | <A NAME="easydialogs"><CODE>EasyDialogs</CODE></A> is a handy standard | 
					
						
							|  |  |  | module that provides you with routines that put up common text-only | 
					
						
							|  |  |  | modal dialogs: | 
					
						
							|  |  |  | <UL> | 
					
						
							|  |  |  | <LI> <CODE>Message(str)</CODE> | 
					
						
							|  |  |  | displays the message "str" and an OK button, | 
					
						
							|  |  |  | <LI> <CODE>AskString(prompt, default)</CODE> | 
					
						
							|  |  |  | asks for a string, displays OK and Cancel buttons, | 
					
						
							|  |  |  | <LI> <CODE>AskYesNoCancel(question, default)</CODE> | 
					
						
							|  |  |  | displays a question and Yes, No and Cancel buttons. | 
					
						
							|  |  |  | </UL> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <A NAME="res"><CODE>Res</CODE></A> is a pretty complete interface to | 
					
						
							|  |  |  | the MacOS Resource Manager, described fully in Inside Mac. There is | 
					
						
							|  |  |  | currently no documentation of it, but the Apple documentation (or | 
					
						
							|  |  |  | Think Ref) will help you on your way if you remember two points: | 
					
						
							|  |  |  | <UL> | 
					
						
							|  |  |  | <LI> Resources are implemented as Python objects, and each routine | 
					
						
							|  |  |  | with a resource first argument is implemented as a python method. | 
					
						
							|  |  |  | <LI> When in doubt about the arguments examine the routines docstring, | 
					
						
							|  |  |  | as in <CODE>print Res.OpenResFile.__doc__</CODE> | 
					
						
							|  |  |  | </UL> | 
					
						
							|  |  |  |   	 | 
					
						
							|  |  |  | Similarly, <A NAME="dlg"><CODE>Dlg</CODE></A> is an interface to the | 
					
						
							|  |  |  | Dialog manager (with Dialogs being implemented as python objects and | 
					
						
							|  |  |  | routines with Dialog arguments being methods). The sys module you | 
					
						
							|  |  |  | know, I hope.  <A NAME="interslip"><CODE>Interslip</CODE></A>, | 
					
						
							|  |  |  | finally, is the module with the interface to the InterSLIP driver. We | 
					
						
							|  |  |  | use four calls from it: | 
					
						
							|  |  |  | <UL> | 
					
						
							|  |  |  | <LI> <CODE>open()</CODE> | 
					
						
							|  |  |  | opens the driver | 
					
						
							|  |  |  | <LI> <CODE>connect()</CODE> | 
					
						
							|  |  |  | asks it to initiate a connection procedure (without waiting) | 
					
						
							|  |  |  | <LI> <CODE>disconnect()</CODE> | 
					
						
							|  |  |  | asks it to initiate a disconnection procedure (without waiting) | 
					
						
							|  |  |  | <LI> <CODE>status()</CODE> | 
					
						
							|  |  |  | returns the current connection status in the form of an integer state, | 
					
						
							|  |  |  | an integer "message sequence number" and a message string. | 
					
						
							|  |  |  | </UL> | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | Next in the source file we get definitions for our dialog resource | 
					
						
							|  |  |  | number and for the item numbers in our dialog. These should match the | 
					
						
							|  |  |  | situation in our resource file InterslipControl-1.rsrc, | 
					
						
							|  |  |  | obviously. Then we get an array converting numeric state codes | 
					
						
							|  |  |  | returned by <CODE>interslip.status()</CODE> to textual messages. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | On to the main program. We start off with opening our resource file, | 
					
						
							|  |  |  | which should live in the same folder as the python source. If we | 
					
						
							|  |  |  | cannot open it we use <CODE>EasyDialogs</CODE> to print a message and | 
					
						
							|  |  |  | exit. You can try it: just move the resource file somewhere else for a | 
					
						
							|  |  |  | moment. Then, we try to open the interslip driver, again catching an | 
					
						
							|  |  |  | error. All modules that raise <A NAME="macos-errors">MacOS error | 
					
						
							|  |  |  | exceptions</A> will pass a 2-tuple to the exception handler with the | 
					
						
							|  |  |  | first item being the numeric <CODE>OSErr</CODE> code and the second | 
					
						
							|  |  |  | one being an informative message. If no informative message is | 
					
						
							|  |  |  | available it will be the rather uninformative <CODE>"MacOS Error | 
					
						
							|  |  |  | -12345"</CODE>, but at least the second item will always be a | 
					
						
							|  |  |  | printable string. Finally we call do_dialog() to do the real work. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <CODE>Do_dialog()</CODE> uses <CODE>Dlg.GetNewDialog()</CODE> to open | 
					
						
							|  |  |  | a dialog window initialized from 'DLOG' resource ID_MAIN and putting | 
					
						
							|  |  |  | it on screen in the frontmost position.  Next, we go into a loop, | 
					
						
							|  |  |  | calling <CODE>Dlg.ModalDialog()</CODE> to wait for the next user | 
					
						
							|  |  |  | action. <CODE>ModalDialog()</CODE> will return us the item number that | 
					
						
							|  |  |  | the user has clicked on (or otherwise activated). It will handle a few | 
					
						
							|  |  |  | slightly more complicated things also, like the user typing into | 
					
						
							|  |  |  | simple textfields, but it will <EM>not</EM> do things like updating | 
					
						
							|  |  |  | the physical appearance of radio buttons, etc. See Inside Mac or | 
					
						
							|  |  |  | another programming guide for how to handle this | 
					
						
							|  |  |  | yourself. Fortunately, our simple application doesn't have to bother | 
					
						
							|  |  |  | with this, since buttons are the only active elements we have. So, we | 
					
						
							|  |  |  | do a simple switch on item number and call the appropriate routine to | 
					
						
							|  |  |  | implement the action requested. Upon the user pressing "quit" we | 
					
						
							|  |  |  | simply leave the loop and, hence, <CODE>do_dialog()</CODE>. This will | 
					
						
							|  |  |  | cause the python dialog object <CODE>my_dlg</CODE> to be deleted and | 
					
						
							|  |  |  | the on-screen dialog to disappear. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <A NAME="dialog-warning">Time for a warning</A>: be very careful what | 
					
						
							|  |  |  | you do as long as a dialog is on-screen.  Printing something, for | 
					
						
							|  |  |  | instance, may suddenly cause the standard output window to appear over | 
					
						
							|  |  |  | the dialog, and since we took no measures to redraw the dialog it will | 
					
						
							|  |  |  | become very difficult to get out of the dialog. Also, command-period | 
					
						
							|  |  |  | may or may not work in this situation. I have also seen crashes in | 
					
						
							|  |  |  | such a situation, probably due to the multiple event loops involved or | 
					
						
							|  |  |  | some oversight in the interpreter. You have been warned. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The implementation of the "update status" command can use a bit more | 
					
						
							|  |  |  | explaining: we get the new information with <CODE>do_status()</CODE> | 
					
						
							|  |  |  | but now we have to update the on-screen dialog to present this | 
					
						
							|  |  |  | information to the user. The <CODE>GetDialogItem()</CODE> method of | 
					
						
							|  |  |  | the dialog returns three bits of information about the given item: its | 
					
						
							|  |  |  | type, its data handle and its rect (the on-screen <CODE>x,y,w,h</CODE> | 
					
						
							|  |  |  | coordinates). We are only interested in the data handle here, on which | 
					
						
							|  |  |  | we call <CODE>SetDialogItemText()</CODE> to set our new text.  Note | 
					
						
							|  |  |  | here that python programmers need not bother with the C-string versus | 
					
						
							|  |  |  | pascal-string controversy: the python glue module knows what is needed | 
					
						
							|  |  |  | and converts the python string to the correct type. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Finally, the three implementation routines <CODE>do_connect()</CODE>, | 
					
						
							|  |  |  | <CODE>do_disconnect()</CODE> and <CODE>do_status()</CODE> are simply | 
					
						
							|  |  |  | boring wrappers around the corresponding interslip methods that will | 
					
						
							|  |  |  | put up a dialog in case of an error. <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | And that concludes our first example of the use of resources and | 
					
						
							|  |  |  | dialogs. Next, you could have a look at the source of EasyDialogs for | 
					
						
							|  |  |  | some examples of using input fields and filterprocs. Or, go on with | 
					
						
							|  |  |  | reading the <A HREF="example2.html">second part</A> of this document | 
					
						
							|  |  |  | to see how to implement a better version of this application. Not only | 
					
						
							|  |  |  | will it allow the user to go back to the finder (or other apps) when | 
					
						
							|  |  |  | your application is running, it will also free her of the RSI-inducing | 
					
						
							|  |  |  | chore of pressing "update status" continuously... <p> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 |