Best Practices for GUI Scripting

I think you’re confusing Spotlight (a crippled search engine) with NSMetadataQuery (the underlying, and very powerful, API that Spotlight uses, but not to its full extent).

I guess we’ll have to agree to disagree on this. I find Spotlight very powerful.
But then perhaps “powerful” is like “beauty”, varies according to the eyes of the beholder. :wink:

I could add more, but since we are considerably off-topic in our “Spotlight” discussion, I’ll refrain from adding more. I’m all for each person using the tools they prefer.

Hey JM,

Let’s just say that Spotlight has some infuriating and unnecessary limitations…   :sunglasses:

-Chris

1 Like

Hey Bodie,

Sure.

NOTE – This script REQUIRES the Satimage.osax AppleScript Extension to be INSTALLED.

The most recent version can be found (here) – as of today it is at Satimage osax 3.7.0 (build 420).

If you just use the Satimage.osax installer then only one osax package file is installed. (The full Smile installer will install other things as well.)

UI Browser has it’s own keyboard shortcut for sending the current reference to Script Debugger.

R

I have my script bound to:

U

So usage is to send the reference to Script Debugger.

Select the line.

(I have a line-select macro in Keyboard Maestro for that. SD doesn’t have such a command, which is a bit peculiar for a programming editor.)

Run my script via keyboard shortcut.

Hit the button to select the stub token in the code, and go-to-town.

-Chris


Convert UI Browser Flat Ref to Hierarchical Ref v1.00.zip (11.5 KB)

------------------------------------------------------------------------------
# Auth: Christopher Stone
# dCre: 2017/04/05 10:10
# dMod: 2017/04/19 17:01
# Appl: Script Debugger
# Task: Change a flat reference from UI Browser to a vertical hierarchy.
# Libs: None
# Osax: Satimage.osax – http://tinyurl.com/satimage-osaxen OR http://tinyurl.com/smile-beta-page
# Tags: @Applescript, @Script, @Script_Debugger, @Satimage.osax, @Change, @Flat, @Reference, @UI_Browser, @Vertical, @Hierarchy
# Vers: 1.00
------------------------------------------------------------------------------

set tabPad to "																				"
set stubToken to "#" & "~stub~" & "#"

set myPath to (path to me as text)
if myPath ends with "Script Debugger.app:" then error "This script must be saved!"

tell application "Script Debugger"
   if myPath = (get file spec of front document as text) then
      set scriptDoc to a reference to document 2
   else
      set scriptDoc to a reference to document 1
   end if
   
   if scriptDoc exists then
      tell scriptDoc
         
         set selectedText to selection
         
         set cmdHierarchy to cng("\\A\\s+|\\s+\\Z", "", selectedText) of me
         set cmdHierarchy to splittext cmdHierarchy using " +of +" with regexp
         set cmdHierarchy to reverse of cmdHierarchy
         set cmdCount to length of cmdHierarchy
         set cmdHierarchy to cng("^", "tell ", cmdHierarchy) of me
         set end of cmdHierarchy to stubToken
         
         repeat with i from 1 to cmdCount
            set end of cmdHierarchy to "end tell"
         end repeat
         
         set cmdCount to (length of cmdHierarchy)
         set linesToIndent to (cmdCount div 2) - 1
         
         repeat with i from 1 to linesToIndent
            set plusItem to (a reference to item (i + 1) of cmdHierarchy)
            set minusItem to (a reference to item (-i - 1) of cmdHierarchy)
            set thePad to text 1 thru i of tabPad
            set (plusItem's contents) to thePad & (plusItem's contents)
            set (minusItem's contents) to thePad & (minusItem's contents)
         end repeat
         
         set blankLine to linesToIndent + 2
         set (item blankLine of cmdHierarchy) to thePad & tab & (item blankLine of cmdHierarchy)
         set cmdHierarchy to cng("^", "\\t", cmdHierarchy) of me
         set beginning of cmdHierarchy to "tell application \"System Events\""
         set end of cmdHierarchy to "end tell"
         set cmdHierarchy to join cmdHierarchy using linefeed
         
         set selection to cmdHierarchy
         
      end tell
   end if
   
end tell

------------------------------------------------------------------------------
--» HANDLERS
------------------------------------------------------------------------------
on cng(_find, _replace, _data)
   change _find into _replace in _data with regexp without case sensitive
end cng
------------------------------------------------------------------------------

That’s something you ought to post in the UI Browser forum too :slight_smile:

It’s the same on my mac as it is on yours, Jim.

Try searching for ‘csrutil’ in Spotlight, and see what it finds. It sure won’t find either the LaunchDaemon in System Library, nor will it find the executable in /usr/bin.

There’s nothing ‘in the eye of the beholder’ about that. It’s just a fact. Spotlight doesn’t search those places (and many others). Spotlight also doesn’t return certain files, even if they’re in a place it searches.

we are considerably off-topic

Noted.

@ccstone You mentioned 600+ scripts in FastScript that are all hot-key driven… What system do you use for your hotkey shortcuts that scales up that well? I have purchased FastScript and am wanting to develop a logical system of my scripts from the get-go, and would love some advice.

Anyone else, please jump in as well!

Thanks!

Hey Phil,

This particular bugaboo is easy to overcome in Spotlight, but you have to know how.

The image depicts two bootable drives on my system.

Not only do you have to add “System files” to the search, but you have to know to enable them in the “other…” section of the pop-up search-criteria-menu.

No normal Mac-user is likely to figure this out. You have to be an enthusiast or pro who studies Mac-publications.

HoudahSpot on the other hand finds csrutil on the first try without special settings…

-Chris

@sphil, @ccstone, et al:

I have created a new topic where we can discuss Spotlight without polluting this thread:
###Mac Search Tools

ATTN Forum Moderator: You might want to move all of the Spotlight posts in this thread to the new thread.

1 Like

Hey Bodie,

That of course encompasses many apps.

I try to use keyboard shortcuts that are as mnemonic as possible.

Here is my FastScripts Finder Menu to give an example of the sort of systems I use (click in the image to see it full size – you may have to click and then click again):

I used to put a bunch of stuff in subfolders, but that makes type-select more difficult — so I only use subfolders here and there.

-Chris

Wow! I could never remember all of those shortcuts. Of course you do have the FS menu.

I use Default Folder X and the Finder Favorites panel for accessing my frequent folders. LaunchBar also works great for this, since I only have to type a few characters in the folder name to select it.

Chris, I know you prefer FastScripts over Keyboard Maestro for triggering a lot of your scripts, but I prefer KM. For some time I have been using KM to assign almost all of my custom shortcuts/hotkeys for all apps, so that I have all of these in one place: KM. I don’t even use the macOS Preferences any more. Having them in KM makes it easy to have all of my Macs using the same shortcuts.

While there is a small performance hit for executing a script from KM, I have not noticed any material delays or differences for my scripts. All seem to run very fast to me. But then I’m using a very fast Mac. I do generally set the KM Action to execute a script file, rather than text.

I also have lots of scripts and macros with keyboard shortcuts assigned (mostly in Keyboard Maestro or an older launcher called Butler, which is not so well-known but which I love for its ability to create custom pop-up menus). A few years ago I started running into the problem of remembering all the shortcuts I had created, so I gave some thought to systematizing them. What I came up with was a list of “objects” such as files, calendar events, contacts, OmniFocus tasks, etc. Most of these “live” mainly, but not necessarily exclusively, in one given application. For most of them there’s a fairly consistent set of tasks that I might want to do with them: open, make new, find, make with selection, find with selection, plus any number of more specific tasks for each object or application.

So, each “object” type is associated with a letter or other key, for example:

  • E = (calendar) event
  • M = e-mail message
  • N = note, i.e. plain-text file
  • U = contact ('uman being)
  • S = script
  • 2 (for “2”-do) = OmniFocus task
  • … etc.

Each type of action I associate with one or more modifier keys:
⌃ plus the letter opens the main application I use for the given object type, e.g.:

  • ⌃E launches/activates Fantastical

  • ⌃M launches Mail.app

  • … etc.

  • ⌃⌥ plus the letter creates a new object, e.g. ⌃⌥M launches Mail and creates a new message, etc.

  • ⇧⌃⌥ plus the letter creates a new object using the current text, image, or file selection (like using the Services menu — in fact a lot of these just call the corresponding Services menu item)

  • ⌃⌘ plus the letter goes to the search field or nearest equivalent in the application for the given object type

  • ⇧⌃⌘ plus the letter copies the current selection, goes to the search field for the object type, and pastes (or uses an equivalent existing Services menu item if one is available)

  • ⇧⌃ plus the letter pops up a menu of actions for the given object type

This leaves ⌃⌥⌘, ⇧⌃⌘, and the infamous finger-tangling ⇧⌃⌥⌘ (as well as combinations that don’t include ⌃ and are thus more likely to clash with built-in shortcuts) for other scripts or macros that don’t fit into this schema.

I prefer to use LaunchBar for launching scripts:

With LaunchBar I don’t need shortcuts. The only thing that LaunchBar understands are Abbreviations. Abbreviations are sequences of letters like MP, MH, FLT, LMGS, and so on.

Compared to keyboard shortcuts, abbreviations have a significant advantage:

You have a huge namespace at your disposal: 26^3 combinations if you limit yourself to three-letter abbreviations, and still 676 two-letter abbreviations. This makes it possible to have really mnemonic abbreviations. With traditional [⌃⌥⇧⌘][a-z]-style shortcuts mnemonics are —at least— difficult.

To make the best use out of it you want to assign a key tap to invoke LB and another key tap to invoke Instant Send to LB. For example I have assigned Single-Shift to invoke LB and Single-Control to invoke Instant Send.

(LB’s default keys are ⌘-Space for LB, and ⌘-Space-hold-down for Instant Send; not so good.)

An example:

Let’s say I have a script or an LB Action called “File Lock Toggle”:

So, to lock/unlock a file I select a file in the Finder, I tap the Control key followed by an F and an L keystroke and Return. Done. Four keystrokes, easy to remember, and I don’t have to hold down any keys simultaneously.

If you do this with traditional shortcuts (via Services, FastScripts or Keyboard Maestro) you likely will have at least two modifier keys plus a letter key, something like ⌃⌘L. Admitted, this is one key less for the shortcut method, but mnemonics are more difficult. (Not so much because of the L but because of the ⌃⌘.). And, with the shortcut method, you have to hold down two keys simultaneously while pressing the letter key, which is a pain if you are not a good touch typist.

In addition: the more scripts you have the more modifier keys you’ll likely have to apply.

I also like running scripts via LaunchBar because…

  • it doesn’t matter where the script is: It can be wrapped into a LB Action, or it can be a standalone script, anywhere on your disk (as long as the directory is indexed by LB, of course).

    • FastScripts (or Apple’s Scripts menulet) forces you to drag an alias or a link into ~/Library/Scripts/
  • I don’t have to worry about the right abbreviation: By default LB generates the abbreviation from the “initials” of the script name (like FL or FLT from “File Lock Toggle” in the above example). If this is not OK, I can always force-apply an arbitrary abbreviation to a script, or I can train LB to understand my preferred abbreviation.

  • If I’ve forgot the mnemonics of my script, I can always type something like “lock” (referring to the example above) and it will still list my script amongst the top ten hits or so.

– Tom

1 Like

Thanks for sharing all that, Tom.
I’ve got to re-look at how I run scripts. I have not been using LB, but I certainly can see the utility.

I’m trying to script making Google Chrome full screen. I had a version using keystrokes, but some users have changed their preferences so those don’t work all the time. Plus, it’s a toggle, so if it’s already full screen, the keystroke will turn that off, so you have to go to the menu anyway (although, I suppose you could get the window size.)

The easy part is going to full screen mode, because the menu item’s name changes to “Exit Full Screen Mode” so it’s not a toggle.

The tricky part is getting the “Always Show Toolbar in Full Screen” right. If there’s a √ at the beginning of the menu item name then it’s on, and I want to turn it off. So I need a way to determine if it’s on or off. Enabled doesn’t work. Not sure what does. (Once this is solved I’ll need to also turn off “Always Show Bookmarks Bar in Full Screen.”

Any suggestions?

tell application "Google Chrome" to activate
delay 1
tell application "System Events"
	tell its application process "Google Chrome"
		tell its menu bar 1
			tell its menu "View"
				tell its menu item "Always Show Toolbar in Full Screen"
					set toolBarShowing to properties
				end tell
			end tell
			if not toolBarShowing then
				click menu item "Always Show Toolbar in Full Screen"
			end if
			click menu item "Enter Full Screen"
		end tell
	end tell
end tell

Tom,

This post has me incredibly intrigued about LaunchBar. To be honest, I have wanted to check it out for years, but I am a committed Alfred user and didn’t see the need for a separate launcher app.

However…

To have a dedicated app to launch scripts, ESPECIALLY in the way that you described by just tapping a few keys (i.e. without developing a massive keyboard shortcut hierarchy) is very interesting. I also appreciated your personal use of the shift and control keys to invoke LB and instant-send. THAT sounds awesome.

I am digging in deep to learn about LaunchBar and I appreciate your contributing your perspective!

Thanks Tom

(Once I get used to LaunchBar, I will probably be hitting you up soon for some more tips and best practices! Thanks)

Just to make things fun, I just discovered that you can search and run Keyboard Maestro actions FROM LAUNCHBAR! This really makes LaunchBar the top choice for activating scripts! I an use Keyboard Maestro or just script files and access them all from LaunchBar! Thanks @Tom for the tip!

The Lauchbar Action that makes this possible is found here:

Hi Bodie,

with the current LaunchBar version this works also out of the box (without mlinzner’s action).

Just type “KM” (or whatever abbreviation LB has learned for Keyboard Maestro), when Keyboard Maestro is selected hit the spacebar. Then you can further forward type to the desired macro.

If you are looking for more tips on LaunchBar have a look at the KM forum, especially this thread, and others.

I would agree that LB offers a lot, and I use it for a lot.

I find this KM Macro to be more useful for triggering KM Macros, since it shows only those Macros that are currently activated:
Execute Macro by Name (Spotlight)

I find that LB is great if you know the name of the item, or at least some characters in the name. But when I don’t remember, LB is not of much help to me.

So, to trigger macros/scripts based on the FrontMost app, I prefer:

The really great thing here is that we have so many great tools to choose from, and each of us can pick the tool we prefer.