Here’s a simple example of calling Keyboard Maestro actions from scripts.
The scripting section of the KM documentation describes how you can find the XML for an action by opening a saved .kmmacros file in a text editor, and then call the XML snippet with the .doScript()
/ do script
method of the “Keyboard Maestro Engine” application.
http://www.keyboardmaestro.com/documentation/7/keyboardmaestro.html#scripting_control
Here are two approaches to selecting (from a script) a particular menu item in a named application.
- by using a KM Action from the script (easier and more reliable)
- by relying on the raw resources of the scripting language (harder to maintain, less reliable)
// Rob Trew (twitter @complexpoint) 2015
// TWO APPROACHES TO CLICKING View > Panes > Split Right IN THE ATOM EDITOR MENU SYSTEM
// THE KM ACTION APPROACH:
menuItemClickKM("Atom", ["View", "Panes", "Split Right"]);
// AND THE RAW SCRIPTING LANGUAGE APPROACH
// (contrasting functions below)
//menuItemClickSE("Atom", ["View", "Panes", "Split Right"]);
// VERSION ONE (BETTER) USING Keyboard Maestro Engine with custom XML
// Calling KME.doScript() on a generalised XML fragment
// (save a KM macro and inspect the XML in the .kmmacros file)
function menuItemClickKM(strAppName, lstMenuPath) {
"use strict";
var appSE = Application("System Events"),
lstApps = appSE.processes.where({
name: strAppName
}),
procApp = lstApps.length ? lstApps[0] : null;
// XML copied and pasted from a .kmmacros file,
// and then slightly generalised
return procApp ?
Application("Keyboard Maestro Engine").doScript(
"<dict><key>IsActive</key><true/><key>IsDisclosed</key><true/><key>MacroActionType</key><string>SelectMenuItem</string><key>Menu</key><array><string>" + lstMenuPath.join("</string><string>") + "</string></array><key>TargetApplication</key><dict><key>BundleIdentifier</key><string>" + procApp.bundleIdentifier() + "</string><key>Name</key><string>" + strAppName + "</string><key>NewFile</key><string>" + procApp.file.posixPath() + "</string></dict><key>TargetingType</key><string>Specific</string></dict>"
) : null;
}
// VERSION TWO ( TRADITIONAL, BUT FEW MERITS BEYOND THAT :-)
// Using System Events - harder to maintain
// less well supported by error messages
// and possibly less reliable, too.
// Exact spelling of application name
// and exact list of menu path strings
// strAppName --> [Menu item name] --> maybeClick
function menuItemClickSE(strAppName, lstMenuPath) {
"use strict";
var appSE = Application("System Events"),
// RUNNING APPLICATION
lstApps = appSE.processes.where({
name: strAppName
}),
procApp = lstApps.length ? lstApps[0] : null,
// MENU ITEM AT END OF PATH
mnuItem = procApp ? (lstMenuPath.slice(1, -1).reduce(
function (oMenu, strMenu) {
return oMenu.menuItems[strMenu].menus[strMenu];
},
procApp.menuBars[0].menus[lstMenuPath[0]]
).menuItems[lstMenuPath[lstMenuPath.length - 1]]) : null;
// ATTEMPTED CLICK
try {
Application(strAppName).activate();
return appSE.click(mnuItem.click()) && true;
} catch (e) {
return e.message +
"\nCheck spelling of application name and menu path:" +
lstMenuPath.join(" > ");
}
}