1 | <HTML><HEAD><TITLE>Using python to create Macintosh applications, part one</TITLE></HEAD>
|
---|
2 | <BODY>
|
---|
3 |
|
---|
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
|
---|
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
|
---|
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 |
|
---|
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>
|
---|
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>,
|
---|
30 | which tells you how to build your own C extension.
|
---|
31 | <p>
|
---|
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,
|
---|
46 | 128-228 are for extensions like Tk,
|
---|
47 | 228-255 for the Python interpreter and 256-511 for standard
|
---|
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 |
|
---|
58 | Okay, let's have a look at dnslookup-1.rsrc, our resource file.
|
---|
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
|
---|
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>
|
---|
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
|
---|
75 | pushed). We handle the actions requested by the user until the Quit
|
---|
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 |
|
---|
87 | The code itself is contained in the file <A
|
---|
88 | HREF="example1/dnslookup-1.py"> dnslookup-1.py</A>. Have
|
---|
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
|
---|
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:
|
---|
129 | <UL>
|
---|
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
|
---|
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
|
---|
138 | situation in our resource file dnslookup-1.rsrc,
|
---|
139 | obviously.<p>
|
---|
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
|
---|
145 | moment. Then we call do_dialog() to do the real work. <p>
|
---|
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
|
---|
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>
|
---|
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 |
|
---|
173 | The implementation of the "Lookup" command can use a bit more
|
---|
174 | explaining: we get the necessary information with <CODE>dnslookup()</CODE>
|
---|
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 |
|
---|
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
|
---|
189 | to see how to implement a better version of this application.<p>
|
---|
190 |
|
---|
191 | </BODY>
|
---|
192 | </HTML>
|
---|
193 |
|
---|