diff options
-rw-r--r-- | Mac/Demo/applescript.html | 171 | ||||
-rw-r--r-- | Mac/Demo/example1.html | 211 | ||||
-rw-r--r-- | Mac/Demo/example2.html | 180 | ||||
-rw-r--r-- | Mac/Demo/index.html | 94 | ||||
-rw-r--r-- | Mac/Demo/plugins.html | 363 |
5 files changed, 1019 insertions, 0 deletions
diff --git a/Mac/Demo/applescript.html b/Mac/Demo/applescript.html new file mode 100644 index 0000000..35cbf49 --- /dev/null +++ b/Mac/Demo/applescript.html @@ -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> diff --git a/Mac/Demo/example1.html b/Mac/Demo/example1.html new file mode 100644 index 0000000..ddb897c --- /dev/null +++ b/Mac/Demo/example1.html @@ -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> + + diff --git a/Mac/Demo/example2.html b/Mac/Demo/example2.html new file mode 100644 index 0000000..3bd2791 --- /dev/null +++ b/Mac/Demo/example2.html @@ -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> diff --git a/Mac/Demo/index.html b/Mac/Demo/index.html new file mode 100644 index 0000000..9ef4ebd --- /dev/null +++ b/Mac/Demo/index.html @@ -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. diff --git a/Mac/Demo/plugins.html b/Mac/Demo/plugins.html new file mode 100644 index 0000000..465603c --- /dev/null +++ b/Mac/Demo/plugins.html @@ -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> |