« Final Shot | Main | Brooklyn Breakfast Mascot »
April 21, 2008
Python Rosetta Stone Automation
Many, many months ago I had a long, long layover in the airport in Madrid. On the flight over from the States I had grown increasingly frustrated with the interface to "The Rosetta Stone" language learning software. Now I must confess, there actually is little to get wrong in this interface as the majority of the interaction with the software is done via clicking one of four buttons. However, I come from the UNIX world where I prefer everything to be able to be driven from a set of keyboard shortcuts and so I committed my layover to automating The Rosetta Stone.
Tonight I find myself writing from yet another long, long layover and I find myself revisiting The Rosetta Stone to brush up on my Spanish. I remembered my little application I wrote in Spain and so I made a few tweaks and decided to share it, just in case anybody should find it useful.
If anything, it demonstrates two concepts:
- The bizarre Windows goop one must wade through in order to automate a Windows application with no known automation interface.
- The power and expressiveness of well-abstracted code written in Python.
Here is the entire application. It is less than 50 meaningful lines of code:
import os import sys import time from msvcrt import getch sys.path.append("../win32automation") import win32automation os.system("title KeySetta") win32automation.spawnProcess( r"C:\Program Files\The Rosetta Stone\The Rosetta Stone\TheRosettaStone.exe") while 1: print "Waiting for application to start..." result = win32automation.windowFocus("The Rosetta Stone") if result: print "Window found!" break time.sleep(1.0) print "Waiting 5 seconds for login screen to appear..." time.sleep(5.0) win32automation.sendKeys('jordanh{ENTER}') print """ Keyboard to Mouse Macros Enabled: - Answer selection: [7] [9] [1] [3] Please focus this window to enable them... """ coord_map = {"7": (180, 300), "9": (480, 300), "1": (180, 475), "3": (480, 475)} while 1: win32automation.windowFocus("KeySetta") ch = getch() print "Last key pressed: %s\r" % (ch), if ch in ('1','3','7','9'): win32automation.windowFocus("The Rosetta Stone") x, y = coord_map[ch] win32automation.mouseMoveToRelative("The Rosetta Stone", x, y) win32automation.mouseClick(button="left") elif ch.lower() == 'q': print "Quitting!" break else: print "WARNING: Unknown key-macro event '%c'." % (ch) sys.exit()
Granted the silly "win32automation" module I wrote is a little stupid. It is based on the excellent example provided over at http://del.icio.us/amoebapr/python. Notice for example none of the functions take an opaque handle to an object. Instead, all of the functions inside of the module require some sort of hint (such as a window title) in order to re-find the desired object before executing an operation on it.
Hopefully however sharing this code will spark somebody to develop a better, free Windows automation module for Python.
Download keysetta.zip (md5sum: a4336d883fcb10b11b8b660451e522a3)
Posted by jordanh at April 21, 2008 11:30 PM
Trackback Pings
TrackBack URL for this entry:
http://jordan.husney.com/mt/mt-tb.cgi/346.

Posting comment...