SLIDE 1
Python and AppleEvents on Mac OS X
Donovan Preston dp@ulaluma.com
SLIDE 2 Survey
- How many people have heard of Python?
- How many people have used Python?
- How many people have heard of AppleScript?
- How many people have used AppleScript?
- How many people have heard of AppleEvents?
- How many people have used AppleEvents?
- Did you think this talk was cancelled?
SLIDE 3 What is Python?
- High level scripting language
- Object oriented
- Written in C
- Very easy to extend in C or C++
- Python makes a very good glue language
- Dynamically Typed
- The type of a variable doesn't matter, but
- bjects have a type
SLIDE 4 What are AppleEvents?
- Language-independent binary IPC protocol
- Applications expose:
- Verbs
- Objects (Components)
- Properties
- AppleEvent data structures are created and
manipulated using a C API
- Events are posted to an app's event queue
- Application handles event, constructs reply
SLIDE 5 Language-Independent?!?
- OSA: The Open Scripting Architecture
- AppleEvents are the low-level description
- f what should occur and what is occurring
- High-level languages use the Dictionary (also
called Terminology) to map between low-level data structures and high-level syntax
- Applications provide a “Dictionary” mapping
between low-level binary codes and english
- 'odoc': open
- 'clos': close
- 'crel': make
SLIDE 6 Python-C Glue
- At the C API Level, AppleEvent data structures
are populated mostly with 32-bit integers
- The MacPython distribution comes with the
extension module “Carbon.AppleEvents”
- This module wraps all C functions required
to create and send AppleEvents
- Using it is tedious
- We want to write Python, not C!
- 4 characters fit in a 32-bit integer, thus all the
Four Character Codes used throughout AE
SLIDE 7
from Carbon import AppleEvents from Carbon import AE ## Create an AEDesc describing the Application we ## want to target, the Finder target = AE.AECreateDesc(AppleEvents.typeApplSignature, 'MACS') ## Create an AEDesc describing the event we want to send, noop noop = AE.AECreateAppleEvent( 'ascr', 'noop', target, AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID) ## Send the event! noop.AESend(AppleEvents.kAEWaitReply, AppleEvents.kAENormalPriority, AppleEvents.kAEDefaultTimeout)
Carbon.AppleEvents Example
SLIDE 8 aepack, aetools, aetypes
- Carbon.AppleEvents is too low level
- The MacPython distribution comes with three
Pure-Python modules which make creating and sending events easier
aepack Conversion to/from Python Types (str, int, list, etc) aetools Creation, packing, sending, unpacking events aetypes Python classes representing AE Types.
SLIDE 9 import aetools, aetypes, aepack ## Send the "activate" to the Finder finder = aetools.TalkTo('MACS') finder.send('misc', 'actv') ## Request the name property from document 1 of OmniGraffle
- mnigraffle = aetools.TalkTo('OGfl')
aedesc, reply, params = omnigraffle.send( 'core', 'getd', {'----': aetypes.Property( 'pnam', fr=aetypes.SelectableItem( 'docu', 1) )} ) ## Print the result print reply['----']
ae* Example
SLIDE 10 Still Too Low-Level!
- Using the ae* modules shortens the code
significantly
- NOT Four Character Codes!
Applications provide the Dictionary (also called Terminology) for translation between Four Character Codes and English (and other languages) — We need to harness this
- But we want to use nice-looking Python objects
and methods
SLIDE 11 gensuitemodule.py
- gensuitemodule is a Python script which
creates “Suite Modules”
- Core Suite
- Verbs: run, open, etc
- Components: document, page, etc
- Properties: document.name, window.name, etc.
- Application Specific Suites
- Application specific verbs, components, and properties
- 'aete' (AppleEvent Terminology) resources are
- rganized into “Suites”
- gensuitemodule parses the aete and generates
Python code—one module per suite
SLIDE 12 Running gensuitemodule
- gensuitemodule is in lib/python2.3/plat-mac
% setenv LIB \ /Library/Frameworks/Python.framework\ /Versions/Current/lib/python2.3/plat-mac % pythonw $LIB/gensuitemodule.py --output iCal \ /Applications/iCal.app
- You will need 2.3b2 Framework Build or later
- The Python package iCal is created, with
- ne Module per suite and an __init__
SLIDE 13
Or...
SLIDE 14
import iCal i = iCal.iCal() ## Creates a custom TalkTo ## Create an ObjectSpecifier specifying the first calendar holidays = iCal.calendar(1) numEvents = i.count(holidays, each=iCal.event) for eventNum in range(numEvents): ## Create a Specifier for the summary property of an event summarySpecifier = holidays.event(eventNum + 1).summary ## Execute the 'get' verb, passing this specifier print i.get(summarySpecifier)
Suite Module Example (use pythonw)
SLIDE 15 gensuitemodule weirdness
- You must iterate items by calling count yourself
and creating ObjectSpecifiers for each
The following code works: i.make( new=iCal.calendar, with_properties={iCal.calendar(1).title: 'hello'}) But is strange because we can't just say 'calendar.title'; we must say 'calendar(1).title'
- You must use get and set, passing a Property
specifier, to retrieve and affect data
- You cannot access a Property instance without
first having an ObjectSpecifier instance
SLIDE 16 A Look Into the Future
- Bob Ippolito and I are planning on doing a
ground-up rewrite of gensuitemodule
- We will take advantage of Python 2.2's
properties and generators
- Python Modules created by this will be
significantly easier to use
- The rewrite will probably not be called
gensuitemodule because it may not be backwards-compatible (API compatible)
SLIDE 17 library = iTunes.source['Library'] for song in library.songs: print song.location library.song[1].name = "SomeName"
Possible Future Syntax
- This nice, natural looking syntax will be
accomplished by:
- Passing a reference to the TalkTo to each ComponentItem
- Using Python 2.2 properties instead of __getattr__ as
ComponentItem and Property factories
- Defining an __iter__ which uses the TalkTo to call count
automatically, and returns n ComponentItems
SLIDE 18
Q & A Interactive Demos