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