Auto-Install Software

Sometimes I find myself having to run a lot of separate installers, and I wondered if I could automate it. A good example is running separate audio plugin installers that all have an identical procedure:

  1. Click Agree/Continue a few times
  2. Deselect unnecessary plugin formats
  3. Enter your password
  4. Click Install

The main sticking point for me at the moment is step 2.

If tickboxes exist in the front window, I'd like to click any whose names contain AAX or VST. I feel like an AppleScript ninja might be able to help with this.

Here's a UI Element reference to the AAX checkbox:

checkbox "VCL-4 Vintage Opto Leveler AAX" of row 2 of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1


With regard to the password window, I'm currently attempting a found image click at every step (failure doesn't abort the macro), but it would be better if I could determine the most recent matching button. Is there a token for which match was found in the previous action? If the last match was Install, then I'd know the password window was coming up next.


Finally, is there a way to exclude a match from the button presses?

In this example, I want to press any button that matches Install but that doesn't match Change or Location.

Auto-Installer.kmmacros (73 KB)

Macro Screenshot

This is why I almost never use flat references to objects – they're too difficult to read and modify.

tell application "System Events"
   tell window 1
      tell group 1
         tell splitter group 1
            tell group 1
               tell scroll area 1
                  tell outline 1
                     tell row 2
                        properties of checkboxes
                     end tell
                  end tell
               end tell
            end tell
         end tell
      end tell
   end tell
end tell

See if you can use the name or description and then write a whose clause.

Perhaps something like this:

checkboxes whose name contains "AAX" or name contains "VST"

No, you'll have to roll your own.

I'd probably use UI-Scripting for this myself, but it depends – sometimes KM's button-exists and button-press are very convenient.

1 Like

I usually get them from UI Browser, so they come flat. The arrowhead method is clearly much better though. :+1:t3:

AS
tell application "System Events"
	tell process "Installer"
		tell window 1
			tell group 1
				tell splitter group 1
					tell group 1
						tell scroll area 1
							tell outline 1
								tell rows
									properties of checkboxes
								end tell
							end tell
						end tell
					end tell
				end tell
			end tell
		end tell
	end tell
end tell

...gives me...

AS Output
minimum value:missing value, orientation:missing value, position:849, 338, class:checkbox, accessibility description:missing value, role description:tickbox, focused:false, title:TCS-68 Cassette Tape Channel AU, size:236, 16, help:missing value, entire contents:, enabled:true, maximum value:missing value, role:AXCheckBox, value:1, subrole:missing value, selected:missing value, name:TCS-68 Cassette Tape Channel AU, description:tickbox, minimum value:missing value, orientation:missing value, position:849, 356, class:checkbox, accessibility description:missing value, role description:tickbox, focused:false, title:TCS-68 Cassette Tape Channel AAX, size:236, 16, help:missing value, entire contents:, enabled:true, maximum value:missing value, role:AXCheckBox, value:1, subrole:missing value, selected:missing value, name:TCS-68 Cassette Tape Channel AAX, description:tickbox, minimum value:missing value, orientation:missing value, position:849, 374, class:checkbox, accessibility description:missing value, role description:tickbox, focused:false, title:TCS-68 Cassette Tape Channel VST3, size:236, 16, help:missing value, entire contents:, enabled:true, maximum value:missing value, role:AXCheckBox, value:1, subrole:missing value, selected:missing value, name:TCS-68 Cassette Tape Channel VST3, description:tickbox, minimum value:missing value, orientation:missing value, position:849, 392, class:checkbox, accessibility description:missing value, role description:tickbox, focused:false, title:TCS-68 Cassette Tape Channel VST2.4, size:236, 16, help:missing value, entire contents:, enabled:true, maximum value:missing value, role:AXCheckBox, value:1, subrole:missing value, selected:missing value, name:TCS-68 Cassette Tape Channel VST2.4, description:tickbox

...so I tried...

AS
tell application "System Events"
	tell process "Installer"
		tell window 1
			tell group 1
				tell splitter group 1
					tell group 1
						tell scroll area 1
							tell outline 1
								tell rows
									click (every checkbox whose name contains "AAX" or name contains "VST")
								end tell
							end tell
						end tell
					end tell
				end tell
			end tell
		end tell
	end tell
end tell

No cigar. The reason, I'm sure, will be blindingly obvious, won't it. I really should learn this stuff properly!

I have a script that fixes flat references from UI Browser, although I'll have to adapt it to use something other that the Satimage.osax for other people's use...


No – you're unlikely to ever find a case where you can click multiple objects like that...

You'll have to do something like this, and then iterate through the objects.

tell application "System Events"
   tell process "Installer"
      tell window 1
         tell group 1
            tell splitter group 1
               tell group 1
                  tell scroll area 1
                     tell outline 1
                        tell rows
                           set checkBoxList to (every checkbox whose name contains "AAX" or name contains "VST")
                        end tell
                     end tell
                  end tell
               end tell
            end tell
         end tell
      end tell
   end tell
end tell

Am I on the right lines here?

tell application "System Events"
	tell process "Installer"
		
		set checkBoxList to a reference to (every checkbox of rows of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1 whose name contains "AAX" or name contains "VST")
		
		repeat with i from 1 to count of checkBoxList
			set thisCheckbox to item i of checkBoxList
			click thisCheckbox
		end repeat
		
	end tell
end tell

Good news: The clicks happen! :blush:
Bad news: The checkboxes stay checked! :cry:

GIF:

CleanShot 2023-04-18 at 20.31.09

Any idea what I'm doing wrong?

I've tried:

set value of thisCheckbox to 0
and
set value of thisCheckbox to false

...no dice.

Odd thing I noticed:

CleanShot 2023-04-18 at 21.43.24

If I manually select one of the rows, click thisCheckbox
works for that row... :thinking:

Maybe select then click?

		repeat with i from 1 to count of checkBoxList
			set thisCheckbox to item i of checkBoxList
            select thisCheckbox
			click thisCheckbox
		end repeat

...and maybe bang in a short delay 0.2 after each of the select and click actions.

It's almost certainly a quirk of that dialog box -- similar code works fine in eg Outlook's "Print" dialog.

1 Like

Thanks for the suggestion Nige. Unfortunately, it just does the same thing but a bit slower. Hmmph

Did you check to see what the actual value is in both on and off states?

Yeah, it's 1 and 0, so I'm perplexed.

This is Apple's crappy level of dedication to usability – buggy and unfixed...

1 Like

Before I give up on this...

I can return coordinates for a UI element and click it with KM, so is there a way to append coordinates for each found checkbox to a variable within one AS block? Then I'd just have to split it up into pairs (somehow) and use each pair as KM click coordinates.

So near yet so far!

I tried using a For Each loop to click any checkboxes containing a string, and the click worked... but of course it will only return the last checkbox in the loop. That means that it will only click the last of each of match:

So yeah any help with getting all coordinates back out to KM would be lovely.

Here's what I have so far (just the checkbox bit):

Click Checkboxes Containing Strings (If Any Exist in Front Window).kmmacros (35.1 KB)

Macro Screenshot

Apologies for blowing up y'alls notifications, but this is me learning out loud.

Some progress...

tell application "System Events"
	tell process "Installer"
		try
			set positionList to {}
			set checkBoxList to a reference to (every checkbox of rows of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1 whose name contains "AAX" or name contains "VST")
			
			repeat with i from 1 to count of checkBoxList
				set thisCheckbox to item i of checkBoxList
				tell thisCheckbox
					set {xPosition, yPosition} to position
					set end of positionList to {xPosition, yPosition}
				end tell
			end repeat
		end try
		
	end tell
end tell

return positionList

This returns the positions of all string-matched checkboxes as a single line:

1161, 364, 1161, 382, 1161, 400, 1161, 436, 1161, 454, 1161, 472

if I add & return

set end of positionList to {xPosition, yPosition} & return

...it comes back with errant , delimiters:

1161, 364,

, 1161, 382,

, 1161, 400,

, 1161, 436,

, 1161, 454,

, 1161, 472,

Ideally I'd also like to be able to set the text matches from a list set in KM, to make it easy to adjust for different installer types. So, instead of

(every checkbox of rows of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1 whose name contains "AAX" or name contains "VST")

...it would be (pseudocode)...

(every checkbox of rows of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1 whose name <matches KMvarList>)

You're returning a list to Keyboard Maestro, and Keyboard Maestro is coercing it to a string...

You may be able to do something like this:

tell application "System Events"
   tell process "Installer"
      set positionList to (position of every checkbox of rows of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1 whose name contains "AAX" or name contains "VST")
   end tell
end tell

return positionList

Here's a better way to deal with your list of lists:

set positionList to {{1, 2}, {3, 4}, {5, 6}}

set AppleScript's text item delimiters to {","}

repeat with listItem in positionList
   set contents of listItem to listItem as text
end repeat

set AppleScript's text item delimiters to linefeed

return positionList as text

You're going to hate this...

~~Nonsense code~~
tell application "System Events"
	tell application process "Installer"
		activate
		set theFirst to item 1 of (every checkbox of rows of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1)
		click theFirst
	end tell
end tell

...to tick/untick the first plug-in. I reckon if you add that activate line 3 to your "clicks but doesn't change" script you'll be there.

Apologies -- bad testing. It worked because I already had the first row selected, mirroring your results from before.Select row 2 and run the script and it unticks row 2 instead of row 1...

This construct frequently won't work.

tell application "System Events"
   tell application process "Installer"
      activate
      set theFirst to item 1 of (every checkbox of rows of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1)
      click theFirst
   end tell
end tell

To make it work more reliably add get before every.

This forces the list to come into existence before trying to get its item 1.

Example:

tell application "Finder"
   item 1 of (every item of desktop)
end tell

tell application "Finder"
   item 1 of (get every item of desktop)
end tell

You can use the installer command to install a PKG (or multiple PKG) with custom options by specifying the custom options you want via an XML file.

It's relatively simple to set up, but I've only done this manually to set up the parameters for each installer, so I'm not sure it would be helpful for the macro you are trying to build, although It might be possible to automate creating the correct XML files by reading the output of installer -pkg installer.pkg -target / -showChoicesXML , searching the XML output for the desired plugin format and then generating the correct XML for the install via a script. Certainly not a simple solution, but it it might end up being more solid and faster than UI scripting if one took the time to build it out.

1 Like

This gives me the list but with some funny comma activity:

, 849, 356, 849, 374, 849, 392,, 849, 428, 849, 446, 849, 464

I'm lost there I'm afraid. Does {{1, 2}, {3, 4}, {5, 6}} expect there always to be six values? Do I insert it into my existing script after the single-line list has been generated?

Code
tell application "System Events"
	tell process "Installer"
		try
			set positionList to {}
			set checkBoxList to a reference to (every checkbox of rows of outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1 whose name contains "AAX" or name contains "VST")
			
			repeat with i from 1 to count of checkBoxList
				set thisCheckbox to item i of checkBoxList
				tell thisCheckbox
					set {xPosition, yPosition} to position
					set end of positionList to {xPosition, yPosition} --& return
				end tell
			end repeat
		end try

		set positionList to {{1, 2}, {3, 4}, {5, 6}}
		
		set AppleScript's text item delimiters to {","}
		
		repeat with listItem in positionList
			set contents of listItem to listItem as text
		end repeat
		
		set AppleScript's text item delimiters to linefeed
		
		return positionList as text
		
	end tell
end tell

return positionList

That outputs:

1,2

3,4

5,6

Is this just an example that I need to translate into a real script? I'm a two-year-old when it comes to this stuff!

Sounds promising but I wouldn't know where to start! Had a look for a 'how to' and came up short. I suppose "mac installer command" isn't going to get me much...

This is bouncing around in my brain now and I can think of a few ways this could be useful for my workflow so i'm going to explore this a little... i'll post back here if i make any meaningful progress.

1 Like