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