[Pythonmac-SIG] AppleEvents and AEPutParamDesc

Phil Christensen phil at bubblehouse.org
Tue Jul 26 05:38:30 CEST 2005


So I'm in the process of trying to implement the BBEdit ODB suite  
functionality in a PyObjC app, and I've run into some weirdness, and  
I hope someone here might be able to help me.

I've been hacking on the MiniAEFrame.py module that is distributed  
with python, and I've had success receiving the BBEdit's response  
(when a file is saved) through this script. All I did was add a call  
to the following function, just before the call to the _Test()  
constructor

     def openInExternalEditor(_filePath, _lnnum=0, _editor = '/ 
Applications/BBEdit.app'):
         from aem.send import Application
         import struct
         from Carbon.File import FSSpec
         SelectionRange = struct.pack('hhllll', 0, int(_lnnum)-1,  
1,1,0,0)
         Application(_editor).event('aevtodoc',{'----':FSSpec 
(_filePath),'kpos':SelectionRange}).send()

BBEdit opens the requested document, and when I save the file, this  
event prints to the console:

     AppleEvent ('R*ch', 'FMod') for <Carbon.File.Alias object at  
0x3e2b0> Other args: {...

which is definitely what I want.

However, when I bring all this into my PyObjC app, I no longer seem  
to receive events properly. Obviously, I had to make some changes,  
since the MiniApplication class in the MiniAEFrame module also  
implements event handling for menus, about boxes, etc, and this is  
already taken care of by the PyObjC app. This is what my event loop  
looks like now:

     def eventLoop():
         # we poll for apple events here
         got, event = Evt.WaitNextEvent(Events.highLevelEventMask, 1)
         if got:
             print 'got: ' + str(got) + ' event: ' + str(event)
             what, message, when, where, modifiers = event
             if what == MiniAEFrame.kHighLevelEvent:
                 AE.AEProcessAppleEvent(event)
             elif hasattr(MacOS, 'HandleEvent'):
                 MacOS.HandleEvent(event)
             else:
                 print "Unhandled event:", event
         # an instance of the twisted python threadedselectreactor
         reactor.callLater(1, eventLoop)

The event registration is taken care of by a trial subclass of  
MiniAEFrame.AEServer:

     class EventServer(MiniAEFrame.AEServer):
         def __init__(self):
             MiniAEFrame.AEServer.__init__(self)
             # these are more than necessary, but in an attempt to
             # mimic MiniAEFrame as much as possible
             self.installaehandler('aevt', 'oapp', self.other)
             self.installaehandler('aevt', 'quit', self.other)
             self.installaehandler('****', '****', self.other)

         def other(self, _object=None, _class=None, _type=None, **args):
             print 'AppleEvent', (_class, _type), 'for', _object,  
'Other args:', args

However, now this gets various events (usually things like the 'rapp'  
event when the app goes into the background, etc), but definitely  
nothing from BBEdit. I thought perhaps it was because the ODB  
specification says you should pass along a 'keyServerID' parameter to  
the 'odoc' event that signifies a 4-byte char OSType. I wasn't sure  
about how this works under OS X, but I set the 'CFBundleSignature'  
property to 'Phil' in the .app bundle's Info.plist, and tried the  
following when I sent the event:

     def openInExternalEditor(_filePath, _lnnum=0, _editor = '/ 
Applications/BBEdit.app'):
         from aem.send import Application
         import struct
         from Carbon.File import FSSpec
         SelectionRange = struct.pack('hhllll', 0, int(_lnnum)-1,  
1,1,0,0)
         Application(_editor).event('aevtodoc',{'----':FSSpec 
(_filePath),
                                                 
'kpos':SelectionRange, 'keyServerID':'Phil'}).send()

but this constantly generates the following error:

Traceback (most recent call last):
   [snipped cocoa-related garbage]
   File "/Users/phil/Workspace/InnerSpace/cocoa/client/dist/ 
controller.app/Contents/Resources/__boot__.py", line 47, in _run
     execfile(path, globals(), globals())
   File "/Users/phil/Workspace/InnerSpace/cocoa/client/ 
controller.py", line 167, in ?
     openInExternalEditor('/tmp/test-12345')
   File "/Users/phil/Workspace/InnerSpace/cocoa/client/ 
controller.py", line 162, in openInExternalEditor
     Application(_editor).event('aevtodoc',{'----':FSSpec 
(_filePath),'kpos':SelectionRange, 'keyServerID':'Phil'}).send()
   File "/Library/Frameworks/Python.framework/Versions/2.4/lib/ 
python2.4/site-packages/aem/send/__init__.py", line 83, in event
     return self._Event(self._address, event, params, atts,  
resulttype, self._transaction, returnid, self._codecs)
   File "/Library/Frameworks/Python.framework/Versions/2.4/lib/ 
python2.4/site-packages/aem/send/send.py", line 87, in __init__
     self._event.AEPutParamDesc(key, codecs.pack(value))
TypeError: OSType arg must be string of 4 chars

I've tried everything at this point to get it to accept 'Phil', such  
as converting it to an int, or packing it into a 4-byte struct, but  
to no avail. In reality, that shouldn't even be necessary, as I  
grepped the Python source tree, and found that the only function that  
generates a TypeError with that string is in mactoolboxglue.c, and it  
should accept the string 'Phil':

/* Convert a 4-char string object argument to an OSType value */
int
PyMac_GetOSType(PyObject *v, OSType *pr)
{
         if (!PyString_Check(v) || PyString_Size(v) != 4) {
                 PyErr_SetString(PyExc_TypeError,
                         "OSType arg must be string of 4 chars");
                 return 0;
         }
         memcpy((char *)pr, PyString_AsString(v), 4);
         return 1;
}

At any rate, sorry this email is so long, but any help would be  
appreciated. It'd be really great to get this external editor feature  
working...

Incidentally, here are some relevant links:

ODB Suite:
   http://www.barebones.com/support/develop/odbsuite.shtml
MiniAEFrame.py:
   http://cvs.sourceforge.net/viewcvs.py/python/python/dist/src/Lib/ 
plat-mac/MiniAEFrame.py?rev=1.5&view=log


Thanks in advance,

-phil christensen


More information about the Pythonmac-SIG mailing list