Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 1 | <HTML><HEAD><TITLE>Using python to create Macintosh applications, part two</TITLE></HEAD> |
| 2 | <BODY> |
| 3 | <H1>Using python to create Macintosh applications, part two</H1> |
| 4 | <HR> |
| 5 | |
| 6 | In this document we rewrite the application of the <A |
| 7 | HREF="example1.html">previous example</A> to use modeless dialogs. We |
| 8 | will use an application framework, and we will have a look at creating |
| 9 | applets, standalone applications written in Python. <A |
| 10 | HREF="example2/InterslipControl-2.py">Source</A> and resource file (in |
| 11 | binary and <A HREF="example2/InterslipControl-2.rsrc.hqx">BinHex</A> |
| 12 | form for downloading) are available in the folder <A |
Jack Jansen | 3412c5d | 1997-08-27 14:08:22 +0000 | [diff] [blame] | 13 | HREF="example2">example2</A>. <p> |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 14 | |
| 15 | Again, we start with ResEdit to create our dialogs. Not only do we |
| 16 | want a main dialog this time but also an "About" dialog, and we |
| 17 | provide the <A NAME="bundle">BNDL resource</A> and related stuff that |
| 18 | an application cannot be without. (Actually, a python applet can be |
| 19 | without, <A HREF="#no-bundle">see below</A>). "Inside Mac" or various |
| 20 | books on macintosh programming will help here. Also, you can refer to |
| 21 | the resource files provided in the Python source distribution for some |
| 22 | of the python-specific points of BNDL programming: the |
| 23 | "appletbundle.rsrc" file is what is used for creating applets if you |
| 24 | don't provide your own resource file. <p> |
| 25 | |
| 26 | Let's have a look at InterslipControl-2.rsrc, our resource file. First |
| 27 | off, there's the standard BNDL combo. I've picked 'PYTi' as signature |
| 28 | for the application. I tend to pick PYT plus one lower-case letter for |
| 29 | my signatures. The finder gets confused if you have two applications |
| 30 | with the same signature. This may be due to some incorrectness on the |
Jack Jansen | 3412c5d | 1997-08-27 14:08:22 +0000 | [diff] [blame] | 31 | side of "BuildApplet", I am not sure. There is one case when you |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 32 | definitely need a unique signature: when you create an applet that has |
| 33 | its own data files and you want the user to be able to start your |
| 34 | applet by double-clicking one of the datafiles. <p> |
| 35 | |
| 36 | There's little to tell about the BNDL stuff: I basically copied the |
| 37 | generic Python applet icons and pasted in the symbol for |
| 38 | InterSLIP. The two dialogs are equally unexciting: dialog 512 is our |
| 39 | main window which has four static text fields (two of which we will be |
| 40 | modifying during runtime, to show the status of the connection) and |
| 41 | two buttons "connect" and "disconnect". The "quit" and "update status" |
| 42 | buttons have disappeared, because they are handled by a menu choice |
| 43 | and automatically, respectively. <p> |
| 44 | |
| 45 | <H2>A modeless dialog application using FrameWork</H2> |
| 46 | |
| 47 | On to the source code in <A |
| 48 | HREF="example2/InterslipControl-2.py">InterslipControl-2.py</A>. The |
| 49 | start is similar to our previous example program <A |
| 50 | HREF="example1/InterslipControl-1.py">InterSlipControl-1.py</A>, with |
| 51 | one extra module being imported. To make life more simple we will use |
| 52 | the <CODE>FrameWork</CODE> module, a nifty piece of code that handles |
| 53 | all the gory mac details of event loop programming, menubar |
| 54 | installation and all the other code that is the same for every mac |
| 55 | program in the world. Like most standard modules, FrameWork will run |
| 56 | some sample test code when you invoke it as a main program, so try it |
| 57 | now. It will create a menu bar with an Apple menu with the about box |
| 58 | and a "File" menu with some pythonesque choices (which do nothing |
| 59 | interesting, by the way) and a "Quit" command that works. <p> |
| 60 | |
Jack Jansen | 0836542 | 1996-04-19 15:56:08 +0000 | [diff] [blame] | 61 | <BLOCKQUOTE> |
| 62 | If you have not used <code>FrameWork</code> before you may want to |
| 63 | first take a look at the <A HREF="textedit.html">Pathetic EDitor</A> |
| 64 | example, which builds a minimal text editor using FrameWork and TextEdit. |
| 65 | On the other hand: we don't use many features of FrameWork, so you could |
| 66 | also continue with this document. |
| 67 | </BLOCKQUOTE> |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 68 | |
| 69 | After the imports we get the definitions of resource-IDs in our |
| 70 | resource file, slightly changed from the previous version of our |
| 71 | program, and the state to string mapping. The main program is also |
| 72 | similar to our previous version, with one important exception: we |
| 73 | first check to see whether our resource is available before opening |
| 74 | the resource file. Why is this? Because later, when we will have |
| 75 | converted the script to an applet, our resources will be available in |
| 76 | the applet file and we don't need the separate resource file |
| 77 | anymore. <p> |
| 78 | |
| 79 | Next comes the definition of our main class, |
| 80 | <CODE>InterslipControl</CODE>, which inherits |
| 81 | <CODE>FrameWork.Application</CODE>. The Application class handles the |
| 82 | menu bar and the main event loop and event dispatching. In the |
| 83 | <CODE>__init__</CODE> routine we first let the base class initialize |
| 84 | itself, then we create our modeless dialog and finally we jump into |
Jack Jansen | ef5cd05 | 1996-09-17 12:39:12 +0000 | [diff] [blame] | 85 | the main loop. The main loop continues until we call <CODE>self._quit</CODE>, |
| 86 | which we will do when the user selects "quit". When we create |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 87 | the instance of <CODE>MyDialog</CODE> (which inherits |
| 88 | <CODE>DialogWindow</CODE>, which inherits <CODE>Window</CODE>) we pass |
| 89 | a reference to the application object, this reference is used to tell |
| 90 | Application about our new window. This enables the event loop to keep |
| 91 | track of all windows and dispatch things like update events and mouse |
| 92 | clicks. <p> |
| 93 | |
| 94 | The <CODE>makeusermenus()</CODE> method (which is called sometime |
| 95 | during the Application <CODE>__init__</CODE> routine) creates a File |
| 96 | menu with a Quit command (shortcut command-Q), which will callback to |
Jack Jansen | ef5cd05 | 1996-09-17 12:39:12 +0000 | [diff] [blame] | 97 | our quit() method. <CODE>Quit()</CODE>, in turn, calls <CODE>_quit</CODE> which |
| 98 | causes the mainloop to terminate at a convenient time. <p> |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 99 | |
| 100 | Application provides a standard about box, but we override this by |
| 101 | providing our own <CODE>do_about()</CODE> method which shows an about |
| 102 | box from a resource as a modal dialog. This piece of code should look |
| 103 | familiar to you from the previous example program. That do_about is |
| 104 | called when the user selects About from the Apple menu is, again, |
| 105 | taken care of by the __init__ routine of Application. <p> |
| 106 | |
| 107 | Our main object finally overrides <CODE>idle()</CODE>, the method |
| 108 | called when no event is available. It passes the call on to our dialog |
| 109 | object to give it a chance to update the status fields, if needed. <p> |
| 110 | |
| 111 | The <CODE>MyDialog</CODE> class is the container for our main |
| 112 | window. Initialization is again done by first calling the base class |
| 113 | <CODE>__init__</CODE> function and finally setting two local variables |
| 114 | that are used by <CODE>updatestatus()</CODE> later. <p> |
| 115 | |
| 116 | <CODE>Do_itemhit()</CODE> is called when an item is selected in this |
| 117 | dialog by the user. We are passed the item number (and the original |
| 118 | event structure, which we normally ignore). The code is similar to the |
| 119 | main loop of our previous example program: a switch depending on the |
| 120 | item selected. <CODE>Connect()</CODE> and <CODE>disconnect()</CODE> |
| 121 | are again quite similar to our previous example. <p> |
| 122 | |
| 123 | <CODE>Updatestatus()</CODE> is different, however. It is now |
| 124 | potentially called many times per second instead of only when the |
| 125 | user presses a button we don't want to update the display every time |
| 126 | since that would cause some quite horrible flashing. Luckily, |
| 127 | <CODE>interslip.status()</CODE> not only provides us with a state and |
| 128 | a message but also with a message sequence number. If neither state |
| 129 | nor message sequence number has changed since the last call there is |
| 130 | no need to update the display, so we just return. For the rest, |
| 131 | nothing has changed. <p> |
| 132 | |
| 133 | <H2><IMG SRC="html.icons/mkapplet.gif"><A NAME="applets">Creating applets</A></H2> |
| 134 | |
Jack Jansen | 3412c5d | 1997-08-27 14:08:22 +0000 | [diff] [blame] | 135 | Now let us try to turn the python script into an applet, a standalone |
| 136 | application. This will <em>not</em> work if you have the "classic 68k" |
| 137 | Python distribution, only if you have the cfm68k or PPC distribution. |
| 138 | Actually, "standalone" is probably not the correct term here, since an |
| 139 | applet does still depend on a lot of the python environment: the |
| 140 | PythonCore shared library, the Python Preferences file, the python Lib |
| 141 | folder and any other modules that the main module depends on. It is |
| 142 | possible to get rid of all these dependencies except for the dependency |
| 143 | on PythonCore, but at the moment that is still quite difficult so we |
| 144 | will ignore that possibility for now. By standalone we mean here that |
| 145 | the script has the look-and-feel of an application, including the |
| 146 | ability to have its own document types, be droppable, etc. <p> |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 147 | |
| 148 | The easiest way to create an applet is to take your source file and |
Jack Jansen | 3412c5d | 1997-08-27 14:08:22 +0000 | [diff] [blame] | 149 | drop it onto "BuildApplet", located in the Python home |
| 150 | folder. This will create an applet with the same name as your python |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 151 | source with the ".py" stripped. Also, if a resource file with the same |
| 152 | name as your source but with ".rsrc" extension is available the |
| 153 | resources from that file will be copied to your applet too. If there |
| 154 | is no resource file for your script a set of default resources will be |
Jack Jansen | 3412c5d | 1997-08-27 14:08:22 +0000 | [diff] [blame] | 155 | used, and the applet will have the default creator 'Pyt0'. The latter |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 156 | also happens if you do have a resource file but without the BNDL |
| 157 | combo. <A NAME="no-bundle">Actually</A>, for our example that would |
| 158 | have been the most logical solution, since our applet does not have |
| 159 | its own data files. It would have saved us hunting for an unused |
| 160 | creator code. The only reason for using the BNDL in this case is |
| 161 | having the custom icon, but that could have been done by pasting an |
| 162 | icon on the finder Info window, or by providing an custon icon in your |
| 163 | resource file and setting the "custom icon" finder bit. <p> |
| 164 | |
Jack Jansen | 3412c5d | 1997-08-27 14:08:22 +0000 | [diff] [blame] | 165 | If you need slightly more control over the BuildApplet process you can |
| 166 | double-click it, and you will get dialogs for source and |
Jack Jansen | a630813 | 1996-03-18 13:38:52 +0000 | [diff] [blame] | 167 | destination of the applet. The rest of the process, including locating |
| 168 | the resource file, remains the same. <p> |
| 169 | |
| 170 | Note that though our example application completely bypasses the |
| 171 | normal python user interface this is by no means necessary. Any python |
| 172 | script can be turned into an applet, and all the usual features of the |
| 173 | interpreter still work. <p> |
| 174 | |
| 175 | That's all for this example, you may now return to the <A HREF="index.html"> |
| 176 | table of contents</A> to pick another topic. <p> |