| """MiniAEFrame - A minimal AppleEvent Application framework. | |
| There are two classes: | |
| AEServer -- a mixin class offering nice AE handling. | |
| MiniApplication -- a very minimal alternative to FrameWork.py, | |
| only suitable for the simplest of AppleEvent servers. | |
| """ | |
| from warnings import warnpy3k | |
| warnpy3k("In 3.x, the MiniAEFrame module is removed.", stacklevel=2) | |
| import traceback | |
| import MacOS | |
| from Carbon import AE | |
| from Carbon.AppleEvents import * | |
| from Carbon import Evt | |
| from Carbon.Events import * | |
| from Carbon import Menu | |
| from Carbon import Win | |
| from Carbon.Windows import * | |
| from Carbon import Qd | |
| import aetools | |
| import EasyDialogs | |
| kHighLevelEvent = 23 # Not defined anywhere for Python yet? | |
| class MiniApplication: | |
| """A minimal FrameWork.Application-like class""" | |
| def __init__(self): | |
| self.quitting = 0 | |
| # Initialize menu | |
| self.appleid = 1 | |
| self.quitid = 2 | |
| Menu.ClearMenuBar() | |
| self.applemenu = applemenu = Menu.NewMenu(self.appleid, "\024") | |
| applemenu.AppendMenu("%s;(-" % self.getaboutmenutext()) | |
| if MacOS.runtimemodel == 'ppc': | |
| applemenu.AppendResMenu('DRVR') | |
| applemenu.InsertMenu(0) | |
| self.quitmenu = Menu.NewMenu(self.quitid, "File") | |
| self.quitmenu.AppendMenu("Quit") | |
| self.quitmenu.SetItemCmd(1, ord("Q")) | |
| self.quitmenu.InsertMenu(0) | |
| Menu.DrawMenuBar() | |
| def __del__(self): | |
| self.close() | |
| def close(self): | |
| pass | |
| def mainloop(self, mask = everyEvent, timeout = 60*60): | |
| while not self.quitting: | |
| self.dooneevent(mask, timeout) | |
| def _quit(self): | |
| self.quitting = 1 | |
| def dooneevent(self, mask = everyEvent, timeout = 60*60): | |
| got, event = Evt.WaitNextEvent(mask, timeout) | |
| if got: | |
| self.lowlevelhandler(event) | |
| def lowlevelhandler(self, event): | |
| what, message, when, where, modifiers = event | |
| h, v = where | |
| if what == kHighLevelEvent: | |
| msg = "High Level Event: %r %r" % (code(message), code(h | (v<<16))) | |
| try: | |
| AE.AEProcessAppleEvent(event) | |
| except AE.Error, err: | |
| print 'AE error: ', err | |
| print 'in', msg | |
| traceback.print_exc() | |
| return | |
| elif what == keyDown: | |
| c = chr(message & charCodeMask) | |
| if modifiers & cmdKey: | |
| if c == '.': | |
| raise KeyboardInterrupt, "Command-period" | |
| if c == 'q': | |
| if hasattr(MacOS, 'OutputSeen'): | |
| MacOS.OutputSeen() | |
| self.quitting = 1 | |
| return | |
| elif what == mouseDown: | |
| partcode, window = Win.FindWindow(where) | |
| if partcode == inMenuBar: | |
| result = Menu.MenuSelect(where) | |
| id = (result>>16) & 0xffff # Hi word | |
| item = result & 0xffff # Lo word | |
| if id == self.appleid: | |
| if item == 1: | |
| EasyDialogs.Message(self.getabouttext()) | |
| elif item > 1 and hasattr(Menu, 'OpenDeskAcc'): | |
| name = self.applemenu.GetMenuItemText(item) | |
| Menu.OpenDeskAcc(name) | |
| elif id == self.quitid and item == 1: | |
| if hasattr(MacOS, 'OutputSeen'): | |
| MacOS.OutputSeen() | |
| self.quitting = 1 | |
| Menu.HiliteMenu(0) | |
| return | |
| # Anything not handled is passed to Python/SIOUX | |
| if hasattr(MacOS, 'HandleEvent'): | |
| MacOS.HandleEvent(event) | |
| else: | |
| print "Unhandled event:", event | |
| def getabouttext(self): | |
| return self.__class__.__name__ | |
| def getaboutmenutext(self): | |
| return "About %s\311" % self.__class__.__name__ | |
| class AEServer: | |
| def __init__(self): | |
| self.ae_handlers = {} | |
| def installaehandler(self, classe, type, callback): | |
| AE.AEInstallEventHandler(classe, type, self.callback_wrapper) | |
| self.ae_handlers[(classe, type)] = callback | |
| def close(self): | |
| for classe, type in self.ae_handlers.keys(): | |
| AE.AERemoveEventHandler(classe, type) | |
| def callback_wrapper(self, _request, _reply): | |
| _parameters, _attributes = aetools.unpackevent(_request) | |
| _class = _attributes['evcl'].type | |
| _type = _attributes['evid'].type | |
| if (_class, _type) in self.ae_handlers: | |
| _function = self.ae_handlers[(_class, _type)] | |
| elif (_class, '****') in self.ae_handlers: | |
| _function = self.ae_handlers[(_class, '****')] | |
| elif ('****', '****') in self.ae_handlers: | |
| _function = self.ae_handlers[('****', '****')] | |
| else: | |
| raise 'Cannot happen: AE callback without handler', (_class, _type) | |
| # XXXX Do key-to-name mapping here | |
| _parameters['_attributes'] = _attributes | |
| _parameters['_class'] = _class | |
| _parameters['_type'] = _type | |
| if '----' in _parameters: | |
| _object = _parameters['----'] | |
| del _parameters['----'] | |
| # The try/except that used to be here can mask programmer errors. | |
| # Let the program crash, the programmer can always add a **args | |
| # to the formal parameter list. | |
| rv = _function(_object, **_parameters) | |
| else: | |
| #Same try/except comment as above | |
| rv = _function(**_parameters) | |
| if rv is None: | |
| aetools.packevent(_reply, {}) | |
| else: | |
| aetools.packevent(_reply, {'----':rv}) | |
| def code(x): | |
| "Convert a long int to the 4-character code it really is" | |
| s = '' | |
| for i in range(4): | |
| x, c = divmod(x, 256) | |
| s = chr(c) + s | |
| return s | |
| class _Test(AEServer, MiniApplication): | |
| """Mini test application, handles required events""" | |
| def __init__(self): | |
| MiniApplication.__init__(self) | |
| AEServer.__init__(self) | |
| self.installaehandler('aevt', 'oapp', self.open_app) | |
| self.installaehandler('aevt', 'quit', self.quit) | |
| self.installaehandler('****', '****', self.other) | |
| self.mainloop() | |
| def quit(self, **args): | |
| self._quit() | |
| def open_app(self, **args): | |
| pass | |
| def other(self, _object=None, _class=None, _type=None, **args): | |
| print 'AppleEvent', (_class, _type), 'for', _object, 'Other args:', args | |
| if __name__ == '__main__': | |
| _Test() |