Applescript via Python
Over the years, Apple has fallen in and out of love with Applescript, its "official" scripting language for MacOS. True, Applescript isn't going to go away any day now and true, it is a very simple language and easy to use. But if you don't want to have to learn a new language just to manipulate MacOS, and want to something fully-powered, there is a choice. appscript is a Python interface to Applescript, giving all the power of AS in neat pythonic code::
app('TextEdit').documents['Read Me'].paragraphs[1].get()
appscript is - for the most part - well documented. The developer also supplies two utilities for interrogating application dictionaries (the Applescript interface they provide) and translating Applescript into appscript Python. Unfortunately:
- Some applications don't implement an Applescript interface.
- The translation utility doesn't recognise some common Applescript commands and idioms.
- Some of the advice found on the web for solving some appscript problems is flat-out wrong. (My guess is that the advisors either didn't test their solutions or the API has changed.)
This short script demonstrates appscript and shows
The problem
I have a list of "to do" items, extracted from an organiser program into a different program, Things. Unfortunately, Things doesn't have an Applescript interface. Nor does it have an import facility.
The solution
On the Things forums, Karel provided two Applescripts for converting mail messages into Things todos and was kind enough to supply the source. The below is largely based upon them. I munged my input into a simple text file, with an item per line. This is the file 'igtd.out' below:
from appscript import * import os, time # build the list of input items infile = open ('igtd.out', 'r') items = [x.strip() for x in infile] # get the application and bring it to the front things_app = app('Things') things_app.activate() # move selection to the inbox time.sleep (1) app('System Events').keystroke('0', using=[k.command_down, k.option_down]) for item in items: # create a new item app('System Events').keystroke('n', using=k.command_down) time.sleep(1) # copy text to clipboard os.popen('pbcopy','w').write(item) # paste into new item app('System Events').keystroke('v', using=k.command_down) time.sleep(1) # hit return to close item app('System Events').keystroke('r') time.sleep(1)
Some explanations:
This mimics an user creating a todo item in Things by:
- Moving to Things inbox (command-option-0)
- Creating a new item (command-N)
- Pasting some text in (command-V)
- Hitting return to close the item.
Between actions, a wait may be required to allow the previous action to taken effect. In the original script, this was accomplished with the Applescript command delay 1 and a timeout. appscript can't translate delay but the python equivalent is time.sleep. The 1 second given is plenty, and probably can be reduced or even eliminated.
Sending keystrokes to the front application can be done with app('System Events').keystroke(KEY, using=MODS) where KEY is the keystroke and MODS is either a single or sequence of key modifiers. The key modifiers are constants in the k subpackage of appscript.
Text can be written to the system clipboard with:
os.popen('pbcopy', 'w').write(item)
where ITEM is the text in question.
app('System Events').keystroke('r') is the equivalent of hitting the return key.