mirror of
https://github.com/python/cpython.git
synced 2025-12-11 03:20:01 +00:00
Explanations of the examples here
This commit is contained in:
parent
f4875af09c
commit
a6308130bb
5 changed files with 1019 additions and 0 deletions
171
Mac/Demo/applescript.html
Normal file
171
Mac/Demo/applescript.html
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
<HTML><HEAD><TITLE>Using Open Scripting Extension from Python</TITLE></HEAD>
|
||||||
|
<BODY>
|
||||||
|
<H1>Using Open Scripting Extension from Python</H1>
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
OSA support in Python is still far from complete, and what
|
||||||
|
support there is is likely to change in the forseeable future. Still,
|
||||||
|
there is already enough in place to allow you to do some nifty things
|
||||||
|
to other programs from your python program. <P>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
Actually, when we say "AppleScript" in this document we actually mean
|
||||||
|
"the Open Scripting Architecture", there is nothing
|
||||||
|
AppleScript-specific in the Python implementation. <p>
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
In this example, we will look at a scriptable application, extract its
|
||||||
|
"AppleScript Dictionary" and generate a Python interface module from
|
||||||
|
that and use that module to control the application. Because we want
|
||||||
|
to concentrate on the OSA details we don't bother with a real
|
||||||
|
user-interface for our application. <p>
|
||||||
|
|
||||||
|
The application we are going to script is Eudora Light, a free mail
|
||||||
|
program from <A HREF="http://www.qualcomm.com">QualComm</A>. This is a
|
||||||
|
very versatile mail-reader, and QualComm has an accompanying
|
||||||
|
commercial version once your needs outgrow Eudora Light. Our program
|
||||||
|
will tell Eudora to send queued mail, retrieve mail or quit. <p>
|
||||||
|
|
||||||
|
<H2>Creating the Python interface module</H2>
|
||||||
|
|
||||||
|
There is a tool in the standard distribution that looks through a file
|
||||||
|
for an 'AETE' or 'AEUT' resource, the internal representation of the
|
||||||
|
AppleScript dictionary. This tool is called
|
||||||
|
<CODE>gensuitemodule.py</CODE>, and lives in
|
||||||
|
<CODE>Tools:bgen:ae</CODE>. When we start it, it asks us for an input
|
||||||
|
file and we point it to the Eudora Light executable. It starts parsing
|
||||||
|
the AETE resource, and for each AppleEvent suite it finds it prompts
|
||||||
|
us for the filename of the resulting python module. Remember to change
|
||||||
|
folders for the first module, you don't want to clutter up the Eudora
|
||||||
|
folder with your python interfaces. If you want to skip a suite you
|
||||||
|
press cancel and the process continues with the next suite. In the
|
||||||
|
case of Eudora, you do <EM>not</EM> want to generate the Required
|
||||||
|
suite, because it will be empty. AppleScript understands that an empty
|
||||||
|
suite means "incorporate the whole standard suite by this name",
|
||||||
|
gensuitemodule does not currently understand this. Creating the empty
|
||||||
|
<CODE>Required_Suite.py</CODE> would hide the correct module of that
|
||||||
|
name from our application. <p>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
Time for a sidebar. If you want to re-create
|
||||||
|
<CODE>Required_Suite.py</CODE> or one of the other standard modules
|
||||||
|
you should look in <CODE>System Folder:Extensions:Scripting
|
||||||
|
Additions:Dialects:English Dialect</CODE>, that is where the core
|
||||||
|
AppleEvent dictionaries live. Also, if you are looking for the
|
||||||
|
<CODE>Finder_Suite</CODE> interface: don't look in the finder (it has
|
||||||
|
an old System 7.0 scripting suite), look at the extension <CODE>Finder
|
||||||
|
Scripting Extension</CODE>. <p>
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
Let's glance at the <A
|
||||||
|
HREF="scripting/Eudora_Suite.py">Eudora_Suite.py</A> just created. You
|
||||||
|
may want to open Script Editor alongside, and have a look at how it
|
||||||
|
interprets the dictionary. EudoraSuite.py starts with some
|
||||||
|
boilerplate, then come some dictionaries implementing the OSA
|
||||||
|
Enumerations, then a big class definition with methods for each
|
||||||
|
AppleScript Verb and finally some comments. The Enumerations we will
|
||||||
|
skip, it suffices to know that whenever you have to pass an enumerator
|
||||||
|
to a method you can pass the english name and don't have to bother
|
||||||
|
with the 4-letter type code. So, you can say
|
||||||
|
<CODE><PRE>
|
||||||
|
eudora.notice(occurrence="mail_arrives")
|
||||||
|
</PRE></CODE>
|
||||||
|
instead of the rather more cryptic
|
||||||
|
<CODE><PRE>
|
||||||
|
eudora.notice(occurrence="wArv")
|
||||||
|
</PRE></CODE>
|
||||||
|
|
||||||
|
The <CODE>Eudora_Suite</CODE> class is the bulk of the code
|
||||||
|
generated. For each verb it contains a method. Each method knows what
|
||||||
|
arguments the verb expects, and it makes handy use of the keyword
|
||||||
|
argument scheme introduced in Python 1.3 to present a palatable
|
||||||
|
interface to the python programmer. You will see that each method
|
||||||
|
calls some routines from <CODE>aetools</CODE>, an auxiliary module
|
||||||
|
living in <CODE>Tools:bgen:ae</CODE> which contains some other nifty
|
||||||
|
AppleEvent tools as well. Have a look at it sometime, there is (of
|
||||||
|
course) no documentation yet. <p>
|
||||||
|
|
||||||
|
The other thing you notice is that each method calls
|
||||||
|
<CODE>self.send</CODE>, but no such method is defined. You will have
|
||||||
|
to provide it by subclassing or multiple inheritance, as we shall see
|
||||||
|
later. <p>
|
||||||
|
|
||||||
|
The module ends with some comments. Sadly, gensuitemodule is not yet
|
||||||
|
able to turn the Object Specifiers into reasonable Python code. For
|
||||||
|
now, if you need object specifiers, you will have to use the routines
|
||||||
|
defined in <CODE>aetools.py</CODE> (and <CODE>aetypes.py</CODE>, which
|
||||||
|
it incorporates). You use these in the form <CODE>aetools.Word(10,
|
||||||
|
aetools.Document(1))</CODE> where the corresponding AppleScript
|
||||||
|
terminology would be <CODE>word 10 of the first
|
||||||
|
document</CODE>. Examine the two modules mentioned above along with
|
||||||
|
the comments at the end of your suite module if you need to create
|
||||||
|
more than the standard object specifiers. <p>
|
||||||
|
|
||||||
|
<H2>Using a Python suite module</H2>
|
||||||
|
|
||||||
|
Now that we have created the suite module we can use it in an
|
||||||
|
application. We do this by creating a class that inherits
|
||||||
|
<CODE>Eudora_Suite</CODE> and the <CODE>TalkTo</CODE> class from
|
||||||
|
<CODE>aetools</CODE>. The <CODE>TalkTo</CODE> class is basically a
|
||||||
|
container for the <CODE>send</CODE> method used by the methods from
|
||||||
|
the suite classes. <p>
|
||||||
|
|
||||||
|
Actually, our class will also inherit <CODE>Required_Suite</CODE>,
|
||||||
|
because we also need functionality from that suite: the quit
|
||||||
|
command. Gensuitemodule could have created this completely derived
|
||||||
|
class for us, since it has access to all information needed to build
|
||||||
|
the class but unfortunately it does not do so at the moment. All in
|
||||||
|
all, the heart of our program looks like this:
|
||||||
|
<CODE><PRE>
|
||||||
|
import Eudora_Suite, Required_Suite, aetools
|
||||||
|
|
||||||
|
class Eudora(aetools.TalkTo, Required_Suite.Required_Suite, \
|
||||||
|
Eudora_Suite.Eudora_Suite):
|
||||||
|
pass
|
||||||
|
</PRE></CODE>
|
||||||
|
|
||||||
|
Yes, our class body is <CODE>pass</CODE>, all functionality is already
|
||||||
|
provided by the base classes, the only thing we have to do is glue it
|
||||||
|
together in the right way. <p>
|
||||||
|
|
||||||
|
Looking at the sourcefile <A
|
||||||
|
HREF="scripting/testeudora.py">testeudora.py</A> we see that it starts
|
||||||
|
with some imports (and some <CODE>addpack</CODE> calls to extend
|
||||||
|
<CODE>sys.path</CODE> to include <CODE>Tools:bgen:ae</CODE>, use of
|
||||||
|
<CODE>ni</CODE> should be preferred over <CODE>addpack</CODE> but I
|
||||||
|
have not managed to master it yet). Then we get the class definition
|
||||||
|
for our main object and a constant giving the signature of Eudora. <p>
|
||||||
|
|
||||||
|
This, again, needs a little explanation. There are various ways to
|
||||||
|
describe to AppleScript which program we want to talk to, but the
|
||||||
|
easiest one to use (from Python, at least) is creator
|
||||||
|
signature. Application name would be much nicer, but Python currently
|
||||||
|
does not have a module that interfaces to the Finder database (which
|
||||||
|
would allow us to map names to signatures). The other alternative,
|
||||||
|
<CODE>ChooseApplication</CODE> from the program-to-program toolbox, is
|
||||||
|
also not available from Python at the moment. <p>
|
||||||
|
|
||||||
|
The main program itself is a wonder of simplicity. We create the
|
||||||
|
object that talks to Eudora (passing the signature as argument), ask
|
||||||
|
the user what she wants and call the appropriate method of the talker
|
||||||
|
object. The use of keyword arguments with the same names as used by
|
||||||
|
AppleScript make passing the parameters a breeze. <p>
|
||||||
|
|
||||||
|
The exception handling does need a few comments, though. Since
|
||||||
|
AppleScript is basically a connectionless RPC protocol nothing happens
|
||||||
|
when we create to talker object. Hence, if the destination application
|
||||||
|
is not running we will not notice until we send our first
|
||||||
|
command. There is another thing to note about errors returned by
|
||||||
|
AppleScript calls: even though <CODE>MacOS.Error</CODE> is raised not
|
||||||
|
all of the errors are actually <CODE>OSErr</CODE>-type errors, some
|
||||||
|
are error codes returned by the server application. In that case, the
|
||||||
|
error message will be incorrect. <p>
|
||||||
|
|
||||||
|
That concludes our simple example. Again, let me emphasize that
|
||||||
|
scripting support in Python is not very complete at the moment, and
|
||||||
|
the details of how to use AppleEvents will definitely change in the
|
||||||
|
near future. This will not only fix all the ideosyncracies noted in
|
||||||
|
this document but also break existing programs, since the current
|
||||||
|
suite organization will have to change to fix some of the problems.
|
||||||
|
Still, if you want to experiment with AppleEvents right now: go ahead!
|
||||||
|
<p>
|
||||||
211
Mac/Demo/example1.html
Normal file
211
Mac/Demo/example1.html
Normal file
|
|
@ -0,0 +1,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>
|
||||||
|
|
||||||
|
|
||||||
180
Mac/Demo/example2.html
Normal file
180
Mac/Demo/example2.html
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
<HTML><HEAD><TITLE>Using python to create Macintosh applications, part two</TITLE></HEAD>
|
||||||
|
<BODY>
|
||||||
|
<H1>Using python to create Macintosh applications, part two</H1>
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
In this document we rewrite the application of the <A
|
||||||
|
HREF="example1.html">previous example</A> to use modeless dialogs. We
|
||||||
|
will use an application framework, and we will have a look at creating
|
||||||
|
applets, standalone applications written in Python. <A
|
||||||
|
HREF="example2/InterslipControl-2.py">Source</A> and resource file (in
|
||||||
|
binary and <A HREF="example2/InterslipControl-2.rsrc.hqx">BinHex</A>
|
||||||
|
form for downloading) are available in the folder <A
|
||||||
|
HREF="example2">example2</A>. If you want to run the program on your
|
||||||
|
machine you will also need a new copy of <A
|
||||||
|
HREF="update-to-1.3/FrameWork.py">FrameWork.py</A>, which has been
|
||||||
|
updated since the 1.3 release. <p>
|
||||||
|
|
||||||
|
Again, we start with ResEdit to create our dialogs. Not only do we
|
||||||
|
want a main dialog this time but also an "About" dialog, and we
|
||||||
|
provide the <A NAME="bundle">BNDL resource</A> and related stuff that
|
||||||
|
an application cannot be without. (Actually, a python applet can be
|
||||||
|
without, <A HREF="#no-bundle">see below</A>). "Inside Mac" or various
|
||||||
|
books on macintosh programming will help here. Also, you can refer to
|
||||||
|
the resource files provided in the Python source distribution for some
|
||||||
|
of the python-specific points of BNDL programming: the
|
||||||
|
"appletbundle.rsrc" file is what is used for creating applets if you
|
||||||
|
don't provide your own resource file. <p>
|
||||||
|
|
||||||
|
Let's have a look at InterslipControl-2.rsrc, our resource file. First
|
||||||
|
off, there's the standard BNDL combo. I've picked 'PYTi' as signature
|
||||||
|
for the application. I tend to pick PYT plus one lower-case letter for
|
||||||
|
my signatures. The finder gets confused if you have two applications
|
||||||
|
with the same signature. This may be due to some incorrectness on the
|
||||||
|
side of "mkapplet", I am not sure. There is one case when you
|
||||||
|
definitely need a unique signature: when you create an applet that has
|
||||||
|
its own data files and you want the user to be able to start your
|
||||||
|
applet by double-clicking one of the datafiles. <p>
|
||||||
|
|
||||||
|
There's little to tell about the BNDL stuff: I basically copied the
|
||||||
|
generic Python applet icons and pasted in the symbol for
|
||||||
|
InterSLIP. The two dialogs are equally unexciting: dialog 512 is our
|
||||||
|
main window which has four static text fields (two of which we will be
|
||||||
|
modifying during runtime, to show the status of the connection) and
|
||||||
|
two buttons "connect" and "disconnect". The "quit" and "update status"
|
||||||
|
buttons have disappeared, because they are handled by a menu choice
|
||||||
|
and automatically, respectively. <p>
|
||||||
|
|
||||||
|
<H2>A modeless dialog application using FrameWork</H2>
|
||||||
|
|
||||||
|
On to the source code in <A
|
||||||
|
HREF="example2/InterslipControl-2.py">InterslipControl-2.py</A>. The
|
||||||
|
start is similar to our previous example program <A
|
||||||
|
HREF="example1/InterslipControl-1.py">InterSlipControl-1.py</A>, with
|
||||||
|
one extra module being imported. To make life more simple we will use
|
||||||
|
the <CODE>FrameWork</CODE> module, a nifty piece of code that handles
|
||||||
|
all the gory mac details of event loop programming, menubar
|
||||||
|
installation and all the other code that is the same for every mac
|
||||||
|
program in the world. Like most standard modules, FrameWork will run
|
||||||
|
some sample test code when you invoke it as a main program, so try it
|
||||||
|
now. It will create a menu bar with an Apple menu with the about box
|
||||||
|
and a "File" menu with some pythonesque choices (which do nothing
|
||||||
|
interesting, by the way) and a "Quit" command that works. <p>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
A more complete description of <A NAME="framework">FrameWork</A> is
|
||||||
|
sorely needed, and will (at some point) be incorporated in the
|
||||||
|
programmers manual or in place of this paragraph. For now you'll have
|
||||||
|
to make do with the knowledge that you use FrameWork by building your
|
||||||
|
classes upon the classes provided by it and selectively overriding
|
||||||
|
methods to extend its functionality (or override the default
|
||||||
|
behaviour). And you should read the Source, of Course:-) <p>
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
After the imports we get the definitions of resource-IDs in our
|
||||||
|
resource file, slightly changed from the previous version of our
|
||||||
|
program, and the state to string mapping. The main program is also
|
||||||
|
similar to our previous version, with one important exception: we
|
||||||
|
first check to see whether our resource is available before opening
|
||||||
|
the resource file. Why is this? Because later, when we will have
|
||||||
|
converted the script to an applet, our resources will be available in
|
||||||
|
the applet file and we don't need the separate resource file
|
||||||
|
anymore. <p>
|
||||||
|
|
||||||
|
Next comes the definition of our main class,
|
||||||
|
<CODE>InterslipControl</CODE>, which inherits
|
||||||
|
<CODE>FrameWork.Application</CODE>. The Application class handles the
|
||||||
|
menu bar and the main event loop and event dispatching. In the
|
||||||
|
<CODE>__init__</CODE> routine we first let the base class initialize
|
||||||
|
itself, then we create our modeless dialog and finally we jump into
|
||||||
|
the main loop. The main loop continues until <CODE>self</CODE> is
|
||||||
|
raised, which we will do when the user selects "quit". When we create
|
||||||
|
the instance of <CODE>MyDialog</CODE> (which inherits
|
||||||
|
<CODE>DialogWindow</CODE>, which inherits <CODE>Window</CODE>) we pass
|
||||||
|
a reference to the application object, this reference is used to tell
|
||||||
|
Application about our new window. This enables the event loop to keep
|
||||||
|
track of all windows and dispatch things like update events and mouse
|
||||||
|
clicks. <p>
|
||||||
|
|
||||||
|
The <CODE>makeusermenus()</CODE> method (which is called sometime
|
||||||
|
during the Application <CODE>__init__</CODE> routine) creates a File
|
||||||
|
menu with a Quit command (shortcut command-Q), which will callback to
|
||||||
|
our quit() method. <CODE>Quit()</CODE>, in turn, raises 'self' which
|
||||||
|
causes the mainloop to terminate. <p>
|
||||||
|
|
||||||
|
Application provides a standard about box, but we override this by
|
||||||
|
providing our own <CODE>do_about()</CODE> method which shows an about
|
||||||
|
box from a resource as a modal dialog. This piece of code should look
|
||||||
|
familiar to you from the previous example program. That do_about is
|
||||||
|
called when the user selects About from the Apple menu is, again,
|
||||||
|
taken care of by the __init__ routine of Application. <p>
|
||||||
|
|
||||||
|
Our main object finally overrides <CODE>idle()</CODE>, the method
|
||||||
|
called when no event is available. It passes the call on to our dialog
|
||||||
|
object to give it a chance to update the status fields, if needed. <p>
|
||||||
|
|
||||||
|
The <CODE>MyDialog</CODE> class is the container for our main
|
||||||
|
window. Initialization is again done by first calling the base class
|
||||||
|
<CODE>__init__</CODE> function and finally setting two local variables
|
||||||
|
that are used by <CODE>updatestatus()</CODE> later. <p>
|
||||||
|
|
||||||
|
<CODE>Do_itemhit()</CODE> is called when an item is selected in this
|
||||||
|
dialog by the user. We are passed the item number (and the original
|
||||||
|
event structure, which we normally ignore). The code is similar to the
|
||||||
|
main loop of our previous example program: a switch depending on the
|
||||||
|
item selected. <CODE>Connect()</CODE> and <CODE>disconnect()</CODE>
|
||||||
|
are again quite similar to our previous example. <p>
|
||||||
|
|
||||||
|
<CODE>Updatestatus()</CODE> is different, however. It is now
|
||||||
|
potentially called many times per second instead of only when the
|
||||||
|
user presses a button we don't want to update the display every time
|
||||||
|
since that would cause some quite horrible flashing. Luckily,
|
||||||
|
<CODE>interslip.status()</CODE> not only provides us with a state and
|
||||||
|
a message but also with a message sequence number. If neither state
|
||||||
|
nor message sequence number has changed since the last call there is
|
||||||
|
no need to update the display, so we just return. For the rest,
|
||||||
|
nothing has changed. <p>
|
||||||
|
|
||||||
|
<H2><IMG SRC="html.icons/mkapplet.gif"><A NAME="applets">Creating applets</A></H2>
|
||||||
|
|
||||||
|
Now, if you have a PowerPC Macintosh, let us try to turn the python
|
||||||
|
script into an applet, a standalone application. Actually,
|
||||||
|
"standalone" is probably not the correct term here, since an applet
|
||||||
|
does still depend on a lot of the python environment: the PythonCore
|
||||||
|
shared library, the Python Preferences file, the python Lib folder and
|
||||||
|
any other modules that the main module depends on. It is possible to
|
||||||
|
get rid of all these dependencies except for the dependency on
|
||||||
|
PythonCore, but at the moment that is still quite difficult so we will
|
||||||
|
ignore that possibility for now. By standalone we mean here that the
|
||||||
|
script has the look-and-feel of an application, including the ability
|
||||||
|
to have its own document types, be droppable, etc. <p>
|
||||||
|
|
||||||
|
The easiest way to create an applet is to take your source file and
|
||||||
|
drop it onto "mkapplet" (normally located in the Python home
|
||||||
|
folder). This will create an applet with the same name as your python
|
||||||
|
source with the ".py" stripped. Also, if a resource file with the same
|
||||||
|
name as your source but with ".rsrc" extension is available the
|
||||||
|
resources from that file will be copied to your applet too. If there
|
||||||
|
is no resource file for your script a set of default resources will be
|
||||||
|
used, and the applet will have the default creator 'PYTa'. The latter
|
||||||
|
also happens if you do have a resource file but without the BNDL
|
||||||
|
combo. <A NAME="no-bundle">Actually</A>, for our example that would
|
||||||
|
have been the most logical solution, since our applet does not have
|
||||||
|
its own data files. It would have saved us hunting for an unused
|
||||||
|
creator code. The only reason for using the BNDL in this case is
|
||||||
|
having the custom icon, but that could have been done by pasting an
|
||||||
|
icon on the finder Info window, or by providing an custon icon in your
|
||||||
|
resource file and setting the "custom icon" finder bit. <p>
|
||||||
|
|
||||||
|
If you need slightly more control over the mkapplet process you can
|
||||||
|
double-click mkapplet, and you will get dialogs for source and
|
||||||
|
destination of the applet. The rest of the process, including locating
|
||||||
|
the resource file, remains the same. <p>
|
||||||
|
|
||||||
|
Note that though our example application completely bypasses the
|
||||||
|
normal python user interface this is by no means necessary. Any python
|
||||||
|
script can be turned into an applet, and all the usual features of the
|
||||||
|
interpreter still work. <p>
|
||||||
|
|
||||||
|
That's all for this example, you may now return to the <A HREF="index.html">
|
||||||
|
table of contents</A> to pick another topic. <p>
|
||||||
94
Mac/Demo/index.html
Normal file
94
Mac/Demo/index.html
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
<HTML><HEAD><TITLE>Macintosh Python crash course</TITLE></HEAD>
|
||||||
|
<BODY>
|
||||||
|
<H1><IMG SRC="html.icons/python.gif">Macintosh Python crash course</H1>
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
This set of documents provides an introduction to various aspects of
|
||||||
|
Python programming on the Mac. It is assumed that the reader is
|
||||||
|
already familiar with Python and, to some extent, with MacOS Toolbox
|
||||||
|
programming. Other readers may find something interesting here too,
|
||||||
|
your mileage may vary. <p>
|
||||||
|
|
||||||
|
Another set of Macintosh-savvy examples, more aimed at beginners, is
|
||||||
|
maintained by Joseph Strout, at <A
|
||||||
|
HREF="http://www-acs.ucsd.edu/~jstrout/python/">
|
||||||
|
http://www-acs.ucsd.edu/~jstrout/python/</A>.
|
||||||
|
<P>
|
||||||
|
|
||||||
|
The document was actually written while I was working on a "real"
|
||||||
|
project: creating a single-button application that will allow my
|
||||||
|
girlfriend to read her mail (which actually pass thry <EM>my</EM>
|
||||||
|
mailbox, so I get to read it too, but don't tell her:-) without her
|
||||||
|
having to worry about internet connections, unix commands, etc. The
|
||||||
|
application, when finished, will connect to the net using InterSLIP,
|
||||||
|
start a (pseudo-)POP server on unix using rsh and use AppleScript to
|
||||||
|
tell Eudora to connect to that server and retrieve messages. <p>
|
||||||
|
|
||||||
|
<CITE>
|
||||||
|
If you want to try the examples here you will have to download some
|
||||||
|
fixes to the 1.3 distribution to your Macintosh. You need an updated
|
||||||
|
version of <A HREF="update-to-1.3/FrameWork.py">FrameWork.py</A> (to
|
||||||
|
go in <CODE>Lib:mac</CODE> and updated <A
|
||||||
|
HREF="update-to-1.3/into-PlugIns.hqx">project templates</A> to go into
|
||||||
|
the <CODE>PlugIns</CODE> folder for PPC users.
|
||||||
|
Users of 1.3.1 or later distributions don't need these fixes.<P>
|
||||||
|
</CITE>
|
||||||
|
|
||||||
|
If you are reading this document on the web and would prefer to read
|
||||||
|
it offline you can transfer the whole stuff (as a BinHexed StuffIt
|
||||||
|
archive) from <A HREF="complete.hqx"> here</A>. This archive includes
|
||||||
|
the fixes mentioned in the previous paragraph. <p>
|
||||||
|
|
||||||
|
<H2>Table of contents</H2>
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>
|
||||||
|
<A HREF="example1.html">Using python to create Macintosh applications,
|
||||||
|
part one</A> explains how to create a simple modal-dialog application
|
||||||
|
in Python. It also takes a glance at using the toolbox modules Res and
|
||||||
|
Dlg, and EasyDialogs for simple question-dialogs.
|
||||||
|
|
||||||
|
<LI>
|
||||||
|
<A HREF="example2.html">Using python to create Macintosh applications,
|
||||||
|
part two</A> turns the previous example program into a more complete
|
||||||
|
mac application, using a modeless dialog, menus, etc. It also explains
|
||||||
|
how to create applets, standalone applications written in Python.
|
||||||
|
|
||||||
|
<LI>
|
||||||
|
In the Python distribution two more examples are included without
|
||||||
|
explanation. <I>PICTbrowse</I> is an application that locates PICT
|
||||||
|
resources and displays them, it demonstrates some quickdraw and the
|
||||||
|
resource and list namagers. <I>Imgbrowse</I> displays image files in
|
||||||
|
many different formats (gif, tiff, pbm, etc). It shows how to use the
|
||||||
|
img modules on the mac.
|
||||||
|
|
||||||
|
<LI>
|
||||||
|
<A HREF="plugins.html">Creating a C extension module on the Macintosh</A>
|
||||||
|
is meant for the hardcore programmer, and shows how to create an
|
||||||
|
extension module in C. It also handles using Modulator to create the
|
||||||
|
boilerplate for your module, and creating dynamically-loadable modules
|
||||||
|
on PowerPC Macs.
|
||||||
|
|
||||||
|
<LI>
|
||||||
|
<A HREF="applescript.html">Using Open Scripting Architecture from Python</A> explains
|
||||||
|
how to create a Python module interfacing to a scriptable application,
|
||||||
|
and how to use that module in your python program.
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
At some point in the (possibly distant) future, I will add chapters on
|
||||||
|
how to use bgen to create modules completely automatic and how to make
|
||||||
|
your Python program scriptable, but that will have to wait. <p>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
Please let me know if you miss critical information in this
|
||||||
|
document. I am quite sure that I will never find the time to turn it
|
||||||
|
into a complete MacPython programmers guide (which would probably be a
|
||||||
|
400-page book instead of 5 lousy html-files), but it should contain
|
||||||
|
at least the information that is neither in the standard Python
|
||||||
|
documentation nor in Inside Mac or other Mac programmers
|
||||||
|
documentation. <p>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<A HREF="http://www.cwi.nl/~jack">Jack Jansen</A>,
|
||||||
|
<A HREF="mailto:jack@cwi.nl">jack@cwi.nl</A>, 6-Mar-1996.
|
||||||
363
Mac/Demo/plugins.html
Normal file
363
Mac/Demo/plugins.html
Normal file
|
|
@ -0,0 +1,363 @@
|
||||||
|
<HTML><HEAD><TITLE>Creating a C extension module on the Macintosh</TITLE></HEAD>
|
||||||
|
<BODY>
|
||||||
|
<H1>Creating a C extension module on the Macintosh</H1>
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
This document gives a step-by-step example of how to create a new C
|
||||||
|
extension module on the mac. For this example, we will create a module
|
||||||
|
to interface to the programmers' API of InterSLIP, a package that
|
||||||
|
allows you to use MacTCP (and, hence, all internet services) over a
|
||||||
|
modem connection. <p>
|
||||||
|
|
||||||
|
<H2>Prerequisites</H2>
|
||||||
|
|
||||||
|
There are a few things you need to pull this off. First and foremost,
|
||||||
|
you need a C development environment. Actually, you need a specific
|
||||||
|
development environment, CodeWarrior by <A
|
||||||
|
HREF="http://www.metrowerks.com/">MetroWerks</A>. You will probably
|
||||||
|
need the latest version. You may be able to get by with an older
|
||||||
|
version of CodeWarrior or with another development environment (Up to
|
||||||
|
about 1994 python was developed with THINK C, and in the dim past it
|
||||||
|
was compiled with MPW C) assuming you have managed to get Python to
|
||||||
|
compile under your development environment, but the step-by-step
|
||||||
|
character of this document will be lost. <p>
|
||||||
|
|
||||||
|
Next, you need a <A HREF="http://www.python.org/python/Sources.html">python
|
||||||
|
source distribution</A>. There is a <A
|
||||||
|
HREF="update-to-1.3/into-PlugIns.hqx"> fixed project template</A> that
|
||||||
|
you also need if you are going to make a dynamically loaded
|
||||||
|
module. For PowerPC development you can actually get by without a full
|
||||||
|
source distribution, using the PPC Development distribution (if I have
|
||||||
|
gotten around to putting it together by the time you read
|
||||||
|
this). You'll also need a functional python interpreter, and the
|
||||||
|
Modulator program (which lives in <CODE>Tools:Modulator</CODE> in the
|
||||||
|
standard source distribution). You may also find that Guido's <A
|
||||||
|
HREF="http://www.python.org/doc/ext/ext.html">Extending and embedding
|
||||||
|
the Python interpreter</A> is a very handy piece of documentation. I
|
||||||
|
will skip lots of details that are handled there, like complete
|
||||||
|
descriptions of <CODE>Py_ParseTuple</CODE> and such utility routines,
|
||||||
|
or the general structure of extension modules. <p>
|
||||||
|
|
||||||
|
<H2>InterSLIP and the C API to it</H2>
|
||||||
|
|
||||||
|
InterSLIP, the utility to which we are going to create a python
|
||||||
|
interface, is a system extension that does all the work of connecting
|
||||||
|
to the internet over a modem connection. InterSLIP is provided
|
||||||
|
free-of-charge by <A
|
||||||
|
HREF="http://www.intercon.com/">InterCon</A>. First it connects to
|
||||||
|
your modem, then it goes through the whole process of dialling,
|
||||||
|
logging in and possibly starting the SLIP software on the remote
|
||||||
|
computer and finally it starts with the real work: packing up IP
|
||||||
|
packets handed to it by MacTCP and sending them to the remote side
|
||||||
|
(and, of course, the reverse action of receiving incoming packets,
|
||||||
|
unpacking them and handing them to MacTCP). InterSLIP is a device
|
||||||
|
driver, and you control it using a application supplied with it,
|
||||||
|
InterSLIP Setup. The API that InterSLIP Setup uses to talk to the
|
||||||
|
device driver is published in the documentation and, hence, also
|
||||||
|
useable by other applications. <p>
|
||||||
|
|
||||||
|
I happened to have a C interface to the API, which is all ugly
|
||||||
|
low-level device-driver calls by itself. The C interface is in <A
|
||||||
|
HREF="interslip/InterslipLib.c">InterslipLib.c</A> and <A
|
||||||
|
HREF="interslip/InterslipLib.h">InterslipLib.h</A>, we'll
|
||||||
|
concentrate here on how to build the Python wrapper module around
|
||||||
|
it. Note that this is the "normal" situation when you are writing a
|
||||||
|
Python extension module: you have some sort of functionality available
|
||||||
|
to C programmers and want to make a Python interface to it. <p>
|
||||||
|
|
||||||
|
<H2>Using Modulator</H2>
|
||||||
|
|
||||||
|
The method we describe in this document, using Modulator, is the best
|
||||||
|
method for small interfaces. For large interfaces there is another
|
||||||
|
tool, Bgen, which actually generates the complete module without you
|
||||||
|
lifting a single finger. Bgen, however, has the disadvantage of having
|
||||||
|
a very steep learning curve, so an example using it will have to wait
|
||||||
|
until another document, when I have more time. <p>
|
||||||
|
|
||||||
|
First, let us look at the <A
|
||||||
|
HREF="interslip/InterslipLib.h">InterslipLib.h</A> header file,
|
||||||
|
and see that the whole interface consists of six routines:
|
||||||
|
<CODE>is_open</CODE>, <CODE>is_connect</CODE>,
|
||||||
|
<CODE>is_disconnect</CODE>, <CODE>is_status</CODE>,
|
||||||
|
<CODE>is_getconfig</CODE> and <CODE>is_setconfig</CODE>. Our first
|
||||||
|
step will be to create a skeleton file <A
|
||||||
|
HREF="interslip/@interslipmodule.c">@interslipmodule.c</A>, a
|
||||||
|
dummy module that will contain all the glue code that python expects
|
||||||
|
of an extension module. Creating this glue code is a breeze with
|
||||||
|
modulator, a tool that we only have to tell that we want to create a
|
||||||
|
module with methods of the six names above and that will create the
|
||||||
|
complete skeleton C code for us. <p>
|
||||||
|
|
||||||
|
Why call this dummy module <CODE>@interslipmodule.c</CODE> and not
|
||||||
|
<CODE>interslipmodule.c</CODE>? Self-preservation: if ever you happen
|
||||||
|
to repeat the whole process after you have actually turned the
|
||||||
|
skeleton module into a real module you would overwrite your
|
||||||
|
hand-written code. By calling the dummy module a different name you
|
||||||
|
have to make <EM>two</EM> mistakes in a row before you do this. <p>
|
||||||
|
|
||||||
|
On systems with the Tk windowing API for Python (currently only
|
||||||
|
unix/X11 systems, but mac support may be available when you read this)
|
||||||
|
this is extremely simple. It is actually so simple that it pays to
|
||||||
|
create the skeleton module under unix and ship the code to your
|
||||||
|
mac. You start modulator and are provided with a form in which you
|
||||||
|
fill out the details of the module you are creating. <p>
|
||||||
|
|
||||||
|
<IMG SRC="html.icons/modulator.gif" ALIGN=CENTER><p>
|
||||||
|
|
||||||
|
You'll need to supply a module name (<CODE>interslip</CODE>, in our
|
||||||
|
case), a module abbreviation (<CODE>pyis</CODE>, which is used as a
|
||||||
|
prefix to all the routines and data structures modulator will create
|
||||||
|
for you) and you enter the names of all the methods your module will
|
||||||
|
export (the list above, with <CODE>is_</CODE> stripped off). Note that
|
||||||
|
we use <CODE>pyis</CODE> as the prefix instead of the more logical
|
||||||
|
<CODE>is</CODE>, since the latter would cause our routine names to
|
||||||
|
collide with those in the API we are interfacing to! The method names
|
||||||
|
are the names as seen by the python program, and the C routine names
|
||||||
|
will have the prefix and an underscore prepended. Modulator can do
|
||||||
|
much more, like generating code for objects and such, but that is a
|
||||||
|
topic for a later example. <p>
|
||||||
|
|
||||||
|
Once you have told modulator all about the module you want to create
|
||||||
|
you press "check", which checks that you haven't omitted any
|
||||||
|
information and "Generate code". This will prompt you for a C output
|
||||||
|
file and generate your module for you. <p>
|
||||||
|
|
||||||
|
<H2>Using Modulator without Tk</H2>
|
||||||
|
|
||||||
|
|
||||||
|
Modulator actually uses a two-stage process to create your code: first
|
||||||
|
the information you provided is turned into a number of python
|
||||||
|
statements and then these statements are executed to generate your
|
||||||
|
code. This is done so that you can even use modulator if you don't
|
||||||
|
have Tk support in Python: you'll just have to write the modulator
|
||||||
|
python statements by hand (about 10 lines, in our example) and
|
||||||
|
modulator will generate the C code (about 150 lines, in our
|
||||||
|
example). Here is the Python code you'll want to execute to generate
|
||||||
|
our skeleton module: <p>
|
||||||
|
|
||||||
|
<CODE><PRE>
|
||||||
|
import addpack
|
||||||
|
addpack.addpack('Tools')
|
||||||
|
addpack.addpack('modulator')
|
||||||
|
import genmodule
|
||||||
|
|
||||||
|
m = genmodule.module()
|
||||||
|
m.name = 'interslip'
|
||||||
|
m.abbrev = 'pyis'
|
||||||
|
m.methodlist = ['open', 'connect', 'disconnect', 'status', \
|
||||||
|
'getconfig', 'setconfig']
|
||||||
|
m.objects = []
|
||||||
|
|
||||||
|
fp = open('@interslipmodule.c', 'w')
|
||||||
|
genmodule.write(fp, m)
|
||||||
|
</PRE></CODE>
|
||||||
|
|
||||||
|
Drop this program on the python interpreter and out will come your
|
||||||
|
skeleton module. <p>
|
||||||
|
|
||||||
|
Now, rename the file to interslipmodule.c and you're all set to start
|
||||||
|
developing. The module is complete in the sense that it should
|
||||||
|
compile, and that if you import it in a python program you will see
|
||||||
|
all the methods. It is, of course, not yet complete in a functional
|
||||||
|
way... <p>
|
||||||
|
|
||||||
|
<H2>Adding a module to 68K Python</H2>
|
||||||
|
|
||||||
|
What you do now depends on whether you're developing for PowerPC (or
|
||||||
|
for CFM68K) or for "traditional" mac. For a traditional 68K Python,
|
||||||
|
you will have to add your new module to the project file of the Python
|
||||||
|
interpreter, and you have to edit "config.c" to add the module to the
|
||||||
|
set of builtin modules. In config.c you will add the module at two
|
||||||
|
places: near the start of the file there is a list of external
|
||||||
|
declarations for all init() routines. Add a line of the form
|
||||||
|
<CODE><PRE>
|
||||||
|
extern void initinterslip();
|
||||||
|
</PRE></CODE>
|
||||||
|
here. Further down the file there is an array that is initialized with
|
||||||
|
modulename/initfunction pairs. Add a line of the form
|
||||||
|
<CODE><PRE>
|
||||||
|
{"interslip", initinterslip},
|
||||||
|
</PRE></CODE>
|
||||||
|
here. You may want to bracket these two lines with
|
||||||
|
<CODE><PRE>
|
||||||
|
#ifdef USE_INTERSLIP
|
||||||
|
#endif
|
||||||
|
</PRE></CODE>
|
||||||
|
lines, that way you can easily control whether the module is
|
||||||
|
incorporated into python at compile time. If you decide to do the
|
||||||
|
latter edit your config file (you can find the name in the "C/C++
|
||||||
|
language" section of the MW preferences dialog, it will probably be
|
||||||
|
"mwerks_nonshared_config.h") and add a
|
||||||
|
<CODE><PRE>
|
||||||
|
#define USE_INTERSLIP
|
||||||
|
</PRE></CODE>
|
||||||
|
|
||||||
|
Make the new interpreter and check that you can import the module, see
|
||||||
|
the methods (with "dir(interslip)") and call them. <p>
|
||||||
|
|
||||||
|
<H2>Creating a PowerPC plugin module</H2>
|
||||||
|
|
||||||
|
For PowerPC development you could follow the same path, but it is
|
||||||
|
actually a better idea to use a dynamically loadable module. The
|
||||||
|
advantage of dynamically loadable modules is that they are not loaded
|
||||||
|
until a python program actually uses them (resulting in less memory
|
||||||
|
usage by the interpreter) and that development is a lot simpler (since
|
||||||
|
your projects will all be smaller). Moreover, you can distribute a
|
||||||
|
plugin module by itself without haveing to distribute a complete
|
||||||
|
python interpreter. <p>
|
||||||
|
|
||||||
|
Go to the "PlugIns" folder and copy the files xxmodule.µ,
|
||||||
|
xxmodule_config.h and xxmodule.µ.exp to interslipmodule.µ,
|
||||||
|
interslipmodule_config.h and interslipmodule.µ.exp, respectively. Edit
|
||||||
|
interslipmodule.µ.exp and change the name of the exported routine
|
||||||
|
"initxx" to "initinterslip". Open interslipmodule.µ with CodeWarrior,
|
||||||
|
remove the file xxmodule.c and add interslipmodule.c and make a number
|
||||||
|
of adjustments to the preferences:
|
||||||
|
<UL>
|
||||||
|
<LI> in C/C++ language, set the header file to interslipmodule_config.h
|
||||||
|
<LI> in PPC linker, set the entry point to "initinterslip"
|
||||||
|
<LI> in PPC PEF, set the fragment name to "interslipmodule"
|
||||||
|
<LI> in PPC Project, set the output file name to "interslipmodule.slb".
|
||||||
|
</UL>
|
||||||
|
Next, compile and link your module, fire up python and do the same
|
||||||
|
tests as for 68K python. <p>
|
||||||
|
|
||||||
|
<H2>Getting the module to do real work</H2>
|
||||||
|
|
||||||
|
So far, so good. In half an hour or so we have created a complete new
|
||||||
|
extension module for Python. The downside, however, is that the module
|
||||||
|
does not do anything useful. So, in the next half hour we will turn
|
||||||
|
our beautiful skeleton module into something that is at least as
|
||||||
|
beautiful but also gets some serious work done. For this once,
|
||||||
|
<EM>I</EM> have spent that half hour for you, and you can see the
|
||||||
|
results in <A
|
||||||
|
HREF="interslip/interslipmodule.c">interslipmodule.c</A>. <p>
|
||||||
|
|
||||||
|
We add
|
||||||
|
<CODE><PRE>
|
||||||
|
#include "InterslipLib.h"
|
||||||
|
#include "macglue.h"
|
||||||
|
</PRE></CODE>
|
||||||
|
to the top of the file, and work our way through each of the methods
|
||||||
|
to add the functionality needed. Starting with open, we fill in the
|
||||||
|
template docstring, the value accessible from Python by looking at
|
||||||
|
<CODE>interslip.open.__doc__</CODE>. There are not many tools using
|
||||||
|
this information at the moment, but as soon as class browsers for
|
||||||
|
python become available having this minimal documentation available is
|
||||||
|
a good idea. We put "Load the interslip driver" as the comment
|
||||||
|
here. <p>
|
||||||
|
|
||||||
|
Next, we tackle the body of <CODE>pyis_open()</CODE>. Since it has no
|
||||||
|
arguments and no return value we don't need to mess with that, we just
|
||||||
|
have to add a call to <CODE>is_open()</CODE> and check the return for
|
||||||
|
an error code, in which case we raise an error:
|
||||||
|
<CODE><PRE>
|
||||||
|
err = is_open();
|
||||||
|
if ( err ) {
|
||||||
|
PyErr_Mac(ErrorObject, err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
</PRE></CODE>
|
||||||
|
The routine <CODE><A NAME="PyErr_Mac">PyErr_Mac()</A></CODE> is a
|
||||||
|
useful routine that raises the exception passed as its first
|
||||||
|
argument. The data passed with the exception is based on the standard
|
||||||
|
MacOS error code given, and PyErr_Mac() attempts to locate a textual
|
||||||
|
description of the error code (which sure beats the "error -14021"
|
||||||
|
messages that so many macintosh applications tell their poor
|
||||||
|
users). <p>
|
||||||
|
|
||||||
|
We will skip pyis_connect and pyis_disconnect here, which are pretty
|
||||||
|
much identical to pyis_open: no arguments, no return value, just a
|
||||||
|
call and an error check. With pyis_status() things get interesting
|
||||||
|
again: this call still takes 3 arguments, and all happen to be values
|
||||||
|
returned (a numeric connection status indicator, a message sequence
|
||||||
|
number and a pointer to the message itself, in MacOS pascal-style
|
||||||
|
string form). We declare variables to receive the returned values, do
|
||||||
|
the call, check the error and format the return value. <p>
|
||||||
|
|
||||||
|
Building the return value is done using <CODE><A
|
||||||
|
NAME="Py_BuildValue">Py_BuildValue</A></CODE>:
|
||||||
|
<CODE><PRE>
|
||||||
|
return Py_BuildValue("iiO&", (int)status, (int)seqnum, PyMac_BuildStr255, message);
|
||||||
|
</PRE></CODE>
|
||||||
|
Py_BuildValue() is a very handy routine that builds tuples according
|
||||||
|
to a format string, somewhat similar to the way <CODE>printf()</CODE>
|
||||||
|
works. The format string specifies the arguments expected after the
|
||||||
|
string, and turns them from C objects into python objects. The
|
||||||
|
resulting objects are put in a python tuple object and returned. The
|
||||||
|
"i" format specifier signifies an "int" (hence the cast: status and
|
||||||
|
seqnum are declared as "long", which is what the is_status() routine
|
||||||
|
wants, and even though we use a 4-byte project there is really no
|
||||||
|
reason not to put the cast here). Py_BuildValue and its counterpart
|
||||||
|
Py_ParseTuple have format codes for all the common C types like ints,
|
||||||
|
shorts, C-strings, floats, etc. Also, there is a nifty escape
|
||||||
|
mechanism to format values about which is does not know. This is
|
||||||
|
invoked by the "O&" format: it expects two arguments, a routine
|
||||||
|
pointer and an int-sized data object. The routine is called with the
|
||||||
|
object as a parameter and it should return a python objects
|
||||||
|
representing the data. <CODE>Macglue.h</CODE> declares a number of
|
||||||
|
such formatting routines for common MacOS objects like Str255, FSSpec,
|
||||||
|
OSType, Rect, etc. See the comments in the include file for
|
||||||
|
details. <p>
|
||||||
|
|
||||||
|
<CODE>Pyis_getconfig()</CODE> is again similar to pyis_getstatus, only
|
||||||
|
two minor points are worth noting here. First, the C API return the
|
||||||
|
input and output baudrate squashed together into a single 4-byte
|
||||||
|
long. We separate them out before returning the result to
|
||||||
|
python. Second, whereas the status call returned us a pointer to a
|
||||||
|
<CODE>Str255</CODE> it kept we are responsible for allocating the
|
||||||
|
<CODE>Str255</CODE> for getconfig. This is something that would have
|
||||||
|
been easy to get wrong had we not used prototypes everywhere. Morale:
|
||||||
|
always try to include the header files for interfaces to libraries and
|
||||||
|
other stuff, so that the compiler can catch any mistakes you make. <p>
|
||||||
|
|
||||||
|
<CODE>Pyis_setconfig()</CODE> finally shows off
|
||||||
|
<CODE>Py_ParseTuple</CODE>, the companion function to
|
||||||
|
<CODE>Py_BuildValue</CODE>. You pass it the argument tuple "args"
|
||||||
|
that your method gets as its second argument, a format string and
|
||||||
|
pointers to where you want the arguments stored. Again, standard C
|
||||||
|
types such as strings and integers Py_ParseTuple knows all about and
|
||||||
|
through the "O&" format you can extend the functionality. For each
|
||||||
|
"O&" you pass a function pointer and a pointer to a data area. The
|
||||||
|
function will be called with a PyObject pointer and your data pointer
|
||||||
|
and it should convert the python object to the correct C type. It
|
||||||
|
should return 1 on success and 0 on failure. Again, a number of
|
||||||
|
converters for standard MacOS types are provided, and declared in
|
||||||
|
<CODE>macglue.h</CODE>. <p>
|
||||||
|
|
||||||
|
Next in our source file comes the method table for our module, which
|
||||||
|
has been generated by modulator (and it did a good job too!), but
|
||||||
|
which is worth looking at for a moment. Entries are of the form
|
||||||
|
<CODE><PRE>
|
||||||
|
{"open", pyis_open, 1, pyis_open__doc__},
|
||||||
|
</PRE></CODE>
|
||||||
|
where the entries are python method name, C routine pointer, flags and
|
||||||
|
docstring pointer. The value to note is the 1 for the flags: this
|
||||||
|
signifies that you want to use "new-style" Py_ParseTuple behaviour. If
|
||||||
|
you are writing a new module always use this, but if you are modifying
|
||||||
|
old code which calls something like <CODE>getargs(args, "(ii)",
|
||||||
|
...)</CODE> you will have to put zero here. See "extending and
|
||||||
|
embedding" or possibly the getargs.c source file for details if you
|
||||||
|
need them. <p>
|
||||||
|
|
||||||
|
Finally, we add some code to the init module, to put some symbolic
|
||||||
|
constants (codes that can by returned by the status method) in the
|
||||||
|
module dictionary, so the python program can use "interslip.RUN"
|
||||||
|
instead of the cryptic "4" when it wants to check that the interslip
|
||||||
|
driver is in RUN state. Modulator has already generated code to get at
|
||||||
|
the module dictionary using PyModule_GetDict() to store the exception
|
||||||
|
object, so we simply call
|
||||||
|
<CODE><PRE>
|
||||||
|
PyDict_SetItemString(d, "IDLE", PyInt_FromLong(IS_IDLE));
|
||||||
|
</PRE></CODE>
|
||||||
|
for each of our items. Since the last bit of code in our init routine
|
||||||
|
checks for previous errors with <CODE>PyErr_Occurred()</CODE> and
|
||||||
|
since <CODE>PyDict_SetItemString()</CODE> gracefully handles the case
|
||||||
|
of <CODE>NULL</CODE> parameters (if <CODE>PyInt_FromLong()</CODE>
|
||||||
|
failed, for instance) we don't have to do error checking here. In some
|
||||||
|
other cases you may have to do error checking yourself. <p>
|
||||||
|
|
||||||
|
This concludes our crash-course on writing Python extensions in C on
|
||||||
|
the Macintosh. If you are not done reading yet I suggest you look
|
||||||
|
back at the <A HREF="index.html">MacPython Crashcourse index</A> to
|
||||||
|
find another topic to study. <p>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue