How to list current active macro groups

I use @tiffle 's excellent macro which is akin to a "search within a palette"

Subroutine to Get List of All Macros and IDs in a Specific Macro Group - Macro Library - Keyboard Maestro Discourse

My problem is that I created a variant (described in the discussion above) which is constantly activating and deactivating macro groups / palettes.

This poses a minor problem: from time to time there is a glitch and a macro group is activated by not deactivated which causes casually typing characters which serve as keyboard shortcut in that group to trigger macros. If that happens, I have to identify the problematic active macro group, and that's the task that I want to simplify.

Is there a simple way to list all current active groups.

thanks in advance for your time and help

This is a feature added in v11, in the Keyboard Maestro status menu, select Show Active Macro Groups to show the currently active groups.

3 Likes

thanks very much @peternlewis

1 Like

I hadn't noticed the addition to the status menu Peter pointed out. Super easy!

I have several macros that work with groups using applescript. For example, you can get the names of enabled macro groups like this:

tell application "Keyboard Maestro"
	set EnabledGroups to name of macro groups whose enabled is true
end tell

In case it's useful to see the actions, here's a macro that does the same thing as the status menu "Show Active Macro Groups" using applescript, but could be modified to include all groups, only include smart groups, filter groups, get the UUID instead of or in addition to name, etc.:

Get Enabled Groups by Name.kmmacros (3.9 KB)

2 Likes

Hi @evanfuchs,

Thanks for providing your macro which lists the active macro groups.

After I select the macro group, is it possible to have another prompt with list which displays all the active macros within the selected macro group?

ChrisQ

You might look at this:

1 Like

Using the same approach in the original macro, we can replicate the first three actions but use the variable saved from the first prompt, which contains the name of the selected group, and modify the applescript slightly to create a list of macros in the selected group based on that saved variable:

-- Get the variable where we stored selected group from the first prompt
set kmInst to system attribute "KMINSTANCE"
tell application "Keyboard Maestro Engine"
	set asSelectedGroup to getvariable "Local__SelectedGroup" instance kmInst
end tell

-- Get list of all macros in selected group
tell application "Keyboard Maestro"
	return name of every macro of macro group asSelectedGroup
end tell

Get Enabled Groups→Macros by Name.kmmacros (6.6 KB)

EDIT: I forgot to change the title of the second prompt to something helpful like "Choose Macro to Edit"

1 Like

Thank you so much for this. It's exactly what I was looking for. Much appreciated :slight_smile:

1 Like

Just one more question. Instead of editing the selected macro, could I run the selected macro?

That's so funny. I just revised it for exactly that:

Prompt w/ Enabled Macro Groups→Containing Macros→Edit/Run

1 Like

Fantastic! You read my mind. :slight_smile:

Apologies for resurrecting an old discussion, but I'm trying to understand why I'm not seeing the same results from these scripts as others are.

Simply put, I'm trying to use AppleScript to get the same list of groups as in the "Show Active Macro Groups" item in the KM menu. That result is specific to the foreground application — e.g., if I invoke it in Safari I will see the Safari group, and if I invoke it in the Finder I will get the Finder group, but not vice versa.

When I query "macro groups whose enabled is true" I get the enabled groups for all applications, not just the one in the foreground (and including ones that aren't even running).

Is there a way to write a script to get exactly the results as shown in the KM menu?

Ignore this -- @CRLF shows us the way in the next post. :wink:

But kept for posterity as an indicator of my ignorance, should anyone want to use it against me...

Short answer -- no. The information isn't there to be got.

Longer answer -- sort of. You'll have to pop the KM Status Menu "Show Active Groups" menu item then either OCR the resulting palette or maybe (untested) scrape values with System Events.

Longest answer -- you could do it. But you would have to write a script that parsed every Macro Group's activation XML and compare every condition in there to the current state of your machine. Which is going to take a while (both to write and to run), and will need constant updating unless you manage to account for every potential combination first time.

1 Like

Apologies also for extending this discussion, but it does seem the easily overlooked AppleScript gethotkeys command from the Keyboard Maestro Engine Suite might provide parallel access to the dynamic list of Show Active Macro Groups.

If it does what it says in its AppleScript defintion, it may provide what @dtremit is looking for:

"gethotkeys (verb): Gets an array of groups of arrays of macros that are currently available via hot keys or typed string triggers." (from Keyboard Maestro Engine Suite)

Here is a macro that uses a prompt list to display names and uids from the command's output (an in-memory plist).

Its default is to display gethotkeys groups. I hope the list is close to that of the Show Active Macro Groups in the Status Menu.

Alternatively. it can be set to display the macros in those groups instead. (Set the value of localActiveCollection to "macros")

By default, the prompt list shows macros "currently available via hot keys or typed string triggers" as described in the gethotkeys definition.

Alternatively, it can be set to use the getall parameter described in the AppleScript definition: "retrieves all active macros, not just hot key and typed string triggered ones"
(Set the value of localGetAll to "true")

Active Groups Or Macros in Prompt List.kmmacros (12.5 KB)

Here is the AppleScript

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
property scriptAuthor : "@CRLF"

--┏━━━━━━━━━━━━━━━━━━━━━┓
--┃ Purpose: 
--┃ Get gethotkeys data
--┃ into a uid__name text list for display
--┃  in a prompt list 
--┗━━━━━━━━━━━━━━━━━━━━━┛
if "KMINSTANCE" is in (system attribute) then
	set kminstance to system attribute "KMINSTANCE"
	tell application id "com.stairways.keyboardmaestro.engine"
		set macrosOrGroups to getvariable "localActiveCollection" instance kminstance
		set getAll to (getvariable "localGetAll" instance kminstance) is "true"
	end tell
else --to run as a standalone script, set options here:
	--setttings are: {"groups"|"macros", boolean}
	set {macrosOrGroups, getAll} to {"groups", false}
end if

set activeCollection to GHK(macrosOrGroups, getAll)

--sort by name (sort key = name minus any leading sort specifiers, eg. 01)
set sd to current application's NSSortDescriptor's sortDescriptorWithKey:"sort" ascending:true selector:"localizedCaseInsensitiveCompare:"
set activeCollection to activeCollection's sortedArrayUsingDescriptors:{sd}

-- a dictionary with values of just the keys "sort" and "uid" set to arrays of names and uids
set activeCollection to activeCollection's dictionaryWithValuesForKeys:{"sort", "uid"}
--parallel pairs of arrays
set {names, uids} to (activeCollection's objectsForKeys:{"sort", "uid"} notFoundMarker:"not found") as list

set promptList to current application's NSMutableArray's array()
repeat with i from 1 to (count of names)
	(promptList's addObject:(item i of uids & "__" & item i of names))
end repeat
if macrosOrGroups is "macros" then
	--Get a unique set of uids.
	--(gethotkeys has entries for each trigger, so if a macro has more than one trigger, its uid will appear more than once.)
	set promptList to (current application's NSOrderedSet's orderedSetWithArray:promptList)'s array()
end if
return (promptList's componentsJoinedByString:linefeed) as text

on GHK(macrosOrGroups, getAll)
	tell application id "com.stairways.keyboardmaestro.engine"
		if macrosOrGroups is not in {"macros", "groups"} then error "GHK parameters are: \"groups\" or \"macros\" and true/false"
		--coerce AppleScript property list data to NSData
		if macrosOrGroups is "macros" and getAll is true then
			set theData to (current application's NSArray's arrayWithObject:(gethotkeys with getall))'s firstObject()'s |data|()
		else
			set theData to (current application's NSArray's arrayWithObject:(gethotkeys))'s firstObject()'s |data|()
		end if
	end tell
	--deserialize to plist object. The heirarchy is the same as the Keyboard Maestro Editor: groups containing macros.
	set {groups, theError} to current application's NSPropertyListSerialization's propertyListWithData:theData options:0 format:(missing value) |error|:(reference)
	if groups is missing value then error (theError's localizedDescription() as text) number -10000
	if macrosOrGroups is "groups" then
		return groups
	else if macrosOrGroups is "macros" then
		--unnest macros
		set macros to groups's valueForKeyPath:"@unionOfArrays.macros"
		return macros
	end if
end GHK
3 Likes

Thank you – well found.

FWIW, a JS wrapping returning a list of names:

List of active Macro Group names.kmmacros (5.5 KB)


Expand disclosure triangle to view JS source
const main = () =>
    either(
        alert("Listing of Active Macro Groups")
    )(
        groupNames => groupNames.join("\n")
    )(
        fmapLR(
            xs => xs.map(x => x.name)
        )(
            jsoFromPlistStringLR(
                Application("Keyboard Maestro Engine")
                    .gethotkeys({ asstring: true, getall: true })
            )
        )
    );

// --------------------- MESSAGE ---------------------

// alert :: String => String -> IO String
const alert = title =>
    s => {
        const sa = Object.assign(
            Application("System Events"), {
            includeStandardAdditions: true
        });

        return (
            sa.activate(),
            sa.displayDialog(s, {
                withTitle: title,
                buttons: ["OK"],
                defaultButton: "OK"
            }),
            s
        );
    };

// ---------------------- PLIST ----------------------

// jsoFromPlistStringLR :: XML String -> Either String Dict
const jsoFromPlistStringLR = xml => {
    // Either an explanatory message, or a
    // JS dictionary parsed from the plist XML
    const
        e = $(),
        nsDict = $.NSPropertyListSerialization
            .propertyListWithDataOptionsFormatError(
                $(xml).dataUsingEncoding(
                    $.NSUTF8StringEncoding
                ),
                0, 0, e
            );

    return nsDict.isNil()
        ? Left(
            ObjC.unwrap(
                e.localizedDescription
            )
        )
        : Right(ObjC.deepUnwrap(nsDict));
};

// --------------------- GENERIC ---------------------

// Left :: a -> Either a b
const Left = x => ({
    type: "Either",
    Left: x
});


// Right :: b -> Either a b
const Right = x => ({
    type: "Either",
    Right: x
});


// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = fl =>
    // Application of the function fl to the
    // contents of any Left value in e, or
    // the application of fr to its Right value.
    fr => e => "Left" in e
        ? fl(e.Left)
        : fr(e.Right);


// fmapLR (<$>) :: (b -> c) -> Either a b -> Either a c
const fmapLR = f =>
    // Either f mapped into the contents of any Right
    // value in e, or e unchanged if is a Left value.
    e => "Left" in e
        ? e
        : Right(f(e.Right));

return main();

Sigh... How did I miss that? And working on the plist to get Groups, instead of querying the Editor and so requiring it to be running, is simply brilliant.

However, this is still only picking up a subset of active Macro Groups. This Group, for example:

...will be missed when the macro "another test" is disabled, but included when that macro is enabled.

Which points to an easy workaround. In every Group, include an enabled actionless macro with a will-never-be-typed Typed String trigger :wink:

To make up for my earlier faux pas, here's a macro that pops and scrapes the "Active Macro Groups" palette, presenting the names in a prompt so you can go to the Group in KM Editor:

Pick From Active Macro Groups.kmmacros (4.0 KB)

Image

It's my turn for appreciation and chagrin.

  • Chagrin

The "sort" key/value pair in gethotkeys is in fact is the name + sort specifier eg. 01). "name" omits the specifier.

The getall handler in the script of of the previous macro was doing nothing on the groups option.

It now shows gethotkeys including a group as long as it contains at least one macro with or without a trigger.

The name key is correctly used for the "groups" option.

The "macros" option still gets names from the always-present "sort" key in order to avoid collecting the values of the different macro name keys--"name","namev2","namev3"--present on different types of macro entries: hot key, typed string, USB device triggers.

  • Appreciation

Thank you to @dtremit for his observations and two most generous of contributors, @ComplexPoint and @Nige_S, for providing the feedback that prompted these corrections.

@nige_s original assessment of the problem, still stands, hidden though it be: The exact information of Show Active Groups menu's list is not to be got, short of reading it directly.

The scrape tool he provides that does just is helpful in showing the discrepancies in the gethotkeys list.

I haven't tried too hard to run them down but here are a couple of reasons:

-- Inclusion rules: Active Macro Groups v gethotkeys
-- Empty Active Group .........:white_check_mark:.......................:x:
-- Palette not showing .........:x:.......................:white_check_mark:

Anyway, the revised macro correctly uses gethotkeys's getall option:

Active Groups Or Macros in Prompt List v 01.kmmacros (14.9 KB)

2 Likes