1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
|
<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
extension for PowerPC macs, and may be compiled in your Python
interpreter for 68K macs. As of this writing there is still a slight
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,
128-255 for the Python interpreter and 256-511 for standard
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>
|