Hi all,
Following a bit of a hiatus, I'm now working on the next release of appscript.
Below is a rather rough first draft of a new chapter for the appscript
documentation that'll provide a basic whistle-stop tour of the concepts and
technologies behind application scripting. I'd appreciate any comments or
criticisms folks might have: does it make any sense, does it cover everything
readers need to know, what needs improved/rephrased/explained better/completely
rewritten, does it totally suck, etc?
Many thanks,
has
----------------------------------------------------------------------
===== About Application Scripting =====
This chapter introduces the central concepts behind application scripting.
----- What are Apple events? -----
Apple events are a high-level message-based form of Inter-Process
Communication, used to communicate between local or remote application
processes (and in some cases within the same process).
An Apple event contains typed data describing how the event should be handled
('attributes') such as its name (specified by two OSTypes [1]) and whether or
not a reply is required, and data to be passed as arguments to the
corresponding event handler ('parameters'). Datatypes include common scalar
types (including boolean, integer, float, string, date and file reference),
ordered lists, records (key-value lists where each key is an OSType) and object
specifiers (used to construct first-class queries, commonly referred to as
'application references', that identify objects within an application).
[1] OSType: a 32-bit code, often represented as a 4-character string. Commonly
used by Carbon APIs such as the Apple Event Manager. Mnemonic values are
preferred, e.g. 'docu' = 'document'.
----- What is a scriptable application? -----
A scriptable (or 'AppleScriptable') application is an application that provides
an Apple event interface intended for third-party (e.g. end-user) use. The
application implements one or more event handlers that respond to corresponding
events, and may also support the Apple Event Object Model. While this interface
may be considered an API, the emphasis is on providing a high-level user
interface that is peer to other users interfaces the application may have (GUI,
CLI, web, etc.) and accessible to end-users as much as developers.
----- What is the Apple Event Object Model? -----
The Apple Event Object Model (AEOM) is an idealised, user-oriented
representation of the application's internal data model, allowing clients to
identify and manipulate that structures via Apple events. Incoming Apple events
are unpacked, and any object specifiers are evaluated against the application's
AEOM to identify the user-level object(s) upon which that AE handler should
act.
The AEOM presents user-level objects which may map directly to equivalent
implementation objects (e.g. document, window mapping directly to NSDocument,
NSWindow) or serve as abstract proxies to the actual implementation structures
(e.g. character, word, paragraph mapping indirectly to a character buffer),
depending on which arrangement is most appropriate/convenient for the user.
In AEOM, commands operate upon objects, so unlike DOM a single command may
invoke multiple method calls upon implementation objects in order to perform
its task. Also, where multiple objects are specified, the command should
perform the same action for each of them [assuming a well-implemented AEOM; in
practice most AEOM implementations usually suffer some limitations in this
regard].
----- How does the AEOM work? -----
The AEOM is a tree structure made up of objects. These objects may have
attributes (descriptive values such as class, name, id, size, etc.), e.g.:
app('Finder').version
and may 'contain' other objects, e.g.:
app('Finder').Finder_windows
However, unlike other object models such as DOM, objects within the AEOM are
associated with one another [largely] by relationships rather than simple
physical containment. (Think of AEOM as combining aspects of DOM and relational
database mechanics.) [TO DECIDE: how best to explain attributes such as
app('Finder').Finder_preferences?]
While these relationships may often follow the containment structure of the
underlying data structures, e.g.
app('TextEdit').documents
this is not always the case, e.g.
app('Finder').files
app('Finder').desktop_object.files
app('Finder').disks['MacHD']folders['Users'].folders['John
Brown'].folders['Desktop'].files
would all identify the same objects (files on the user's desktop), though [at
best] only one of these references represents their physical containment
structure.
Similarly, some references may identify different object at different times
according to changes in the application's underlying state, e.g.:
app('iTunes').current_track
while others may identify objects that do not literally exist as individual
entities within the application's underlying data structures, but are
calculated on-the-fly as proxies to the relevant portions of the actual data
structures, e.g.:
app('TextEdit').documents[1].text.characters
app('TextEdit').documents[1].text.words
app('TextEdit').documents[1].text.paragraphs
Relationships may be one-to-one, e.g.:
app('Finder').home
app('iTunes').current_track
or one-to-many, e.g.:
app('Finder').folders
app('TextEdit').documents
Finally, one-to-many relationships may be selective in the objects they
identify, e.g.:
app('Finder').items # identifies all objects that are a subclass of class
'item' (disks, folders, document files, alias files, etc.)
app('Finder').files # identifies all objects that are a subclass of class
'file' (document files, alias files, etc.)
app('Finder').document_files # identifies all objects of class 'document file'
only
----- What is appscript? -----
Appscript is a high-level Python-to-Apple Event Manager bridge, intended for
use by both developers and end-users. The appscript architecture consists of
three layers:
- Carbon.AE -- low-level, largely procedural Python wrapper around the AEM API
- aem -- mid-level wrapper around Carbon.AE, providing an object-oriented API
for building relational AEOM queries and dispatching events
- appscript -- high-level wrapper around aem, providing automatic translation
between human-readable application terminology and corresponding OSType codes,
and representing relational AEOM queries in an OO-like syntax for ease of use.
AEM is largely intended for use by higher-level libraries and developers,
though may also be used by end-users in cases where an application lacks
terminology, or bugs within its terminology prevent its use by appscript.
Appscript is intended for use by both developers and end-users, though
developers may prefer aem for certain tasks as appscript doesn't expose every
aspect of the aem API (send flags) and imposes additional overheads and
dependencies on client code.
e.g. To set the size of the first character of every non-empty paragraph in
every document of TextEdit to 24 pt:
- using appscript:
app('TextEdit').documents.text.paragraphs.filter(its !=
'\n').characters[1].size.set(24)
- using aem:
Application('/Applications/TextEdit.app').event('coregetd', {
'----':
app.elements('docu').property('ctxt').elements('cpar').byfilter(its.ne('\n'))
.elements('cha ').byindex(1).property('ptsz'),
'data': 24
}).send()
(Carbon.AE equivalent not shown due to sheer length and ugliness.)
_______________________________________________
Pythonmac-SIG maillist - [email protected]
http://mail.python.org/mailman/listinfo/pythonmac-sig