Modify AppleScript to hide System Settings GUI

Hello,

Is there a way to modify the script below (which toggles the spellchecker between English and French in System Settings) so that the GUI is not displayed?

on open_settings_to(settings_pane)
	if settings_pane = "Wi-Fi" then
		set settings_pane to "Wi‑Fi"
	end if
	tell application "System Settings"
		activate
	end tell
	tell application "System Events"
		tell application process "System Settings"
			repeat until window 1 exists
				delay 0
			end repeat
			tell splitter group 1 of group 1 of window 1
				tell outline 1 of scroll area 1 of group 1
					set row_names to value of static text of UI element 1 of every row
					repeat with i from 1 to (count row_names)
						if settings_pane is not "Apple ID" then
							if item i of row_names = {settings_pane} then
								log item i of row_names & I
								select row I
								exit repeat
							end if
						else
							try
								if item 1 of item i of row_names contains settings_pane then
									select row I
									exit repeat
								end if
							end try
						end if
					end repeat
				end tell
			end tell
		end tell
	end tell
end open_settings_to

on change_language()
	tell application "System Events"
		tell application process "System Settings"
			tell group 2 of splitter group 1 of group 1 of window 1
				repeat until group 3 of scroll area 1 of group 1 exists
					delay 0
				end repeat
				tell group 3 of scroll area 1 of group 1
					click button 1
				end tell
			end tell
			repeat until group 2 of splitter group 1 of group 1 of sheet 1 of window 1 exists
				delay 0
			end repeat
			tell group 2 of splitter group 1 of group 1 of sheet 1 of window 1
				repeat until group 3 of scroll area 1 exists
					delay 0
				end repeat
				tell group 3 of scroll area 1
					click pop up button 1
					tell menu 1 of pop up button 1
						if selected of menu item "U.S. English" is true then
							click menu item "Français"
							do shell script "osascript -e 'display notification \"Spelling language set to French\" with title \"Keyboard Settings\"'"
						else
							click menu item "U.S. English"
							do shell script "osascript -e 'display notification \"Spelling language set to English\" with title \"Keyboard Settings\"'"
						end if
					end tell
				end tell
				click button 1
			end tell
			delay 0.1
			tell application "System Settings"
				quit
			end tell
		end tell
	end tell
end change_language


on run {}
	open_settings_to("Keyboard")
	change_language()
end run

Cheers.

1 Like
--Running under AppleScript 2.8, MacOS 13.0.1
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "System Settings"
    activate
end tell
tell application "System Events"
    set visible of application process "System Settings" to false
    tell application process "System Settings"
        --your commands
    end tell
end tell

Thank you for the help. Sorry, I’m a layman have next to no idea what I’m doing. I inserted my script under the “your commands” line but am getting a syntax error

Expected “end” or “end tell” but found “on”.

as my script starts with

on open_settings_to(settings_pane)
	if settings_pane = "Wi-Fi" then
		set settings_pane to "Wi‑Fi"
	end if

That’s not really possible, since it’s a handler – and there must be a call to the handler in the main script.

Please post your whole script, so we can see what you’re doing.

You can run or launch an app in the background without activating it.

tell application "System Settings"
   if not running then
      run
      delay 1
   end if
   
   # Other codea
   
end tell

The whole script is in my original post above. It accesses the keyboard tab in System Settings and set the spelling language to French if it is set to English and vice versa. It works totally fine but displays the GUI.

1 Like

Okay then – give this a try…

Code
--------------------------------------------------------
--» MAIN
--------------------------------------------------------

# I never use a run handler, unless I have a need for one.
# For instance – if I also have a n open() handler.

on run {}
   open_settings_to("Keyboard")
   change_language()
end run

--------------------------------------------------------
--» HANDLERS
--------------------------------------------------------
on change_language()
   tell application "System Events"
      tell application process "System Settings"
         tell group 2 of splitter group 1 of group 1 of window 1
            repeat until group 3 of scroll area 1 of group 1 exists
               delay 0
            end repeat
            tell group 3 of scroll area 1 of group 1
               click button 1
            end tell
         end tell
         repeat until group 2 of splitter group 1 of group 1 of sheet 1 of window 1 exists
            delay 0
         end repeat
         tell group 2 of splitter group 1 of group 1 of sheet 1 of window 1
            repeat until group 3 of scroll area 1 exists
               delay 0
            end repeat
            tell group 3 of scroll area 1
               click pop up button 1
               tell menu 1 of pop up button 1
                  if selected of menu item "U.S. English" is true then
                     click menu item "Français"
                     do shell script "osascript -e 'display notification \"Spelling language set to French\" with title \"Keyboard Settings\"'"
                  else
                     click menu item "U.S. English"
                     do shell script "osascript -e 'display notification \"Spelling language set to English\" with title \"Keyboard Settings\"'"
                  end if
               end tell
            end tell
            click button 1
         end tell
         delay 0.1
         tell application "System Settings"
            quit
         end tell
      end tell
   end tell
end change_language
--------------------------------------------------------
on open_settings_to(settings_pane)
   if settings_pane = "Wi-Fi" then
      set settings_pane to "Wi‑Fi"
   end if
   tell application "System Settings"
      if not running then --» •• ccstone ••
         run
         delay 1
      end if
   end tell
   tell application "System Events"
      tell application process "System Settings"
         repeat until window 1 exists
            delay 0
         end repeat
         tell splitter group 1 of group 1 of window 1
            tell outline 1 of scroll area 1 of group 1
               set row_names to value of static text of UI element 1 of every row
               repeat with i from 1 to (count row_names)
                  if settings_pane is not "Apple ID" then
                     if item i of row_names = {settings_pane} then
                        log item i of row_names & i
                        select row i
                        exit repeat
                     end if
                  else
                     try
                        if item 1 of item i of row_names contains settings_pane then
                           select row i
                           exit repeat
                        end if
                     end try
                  end if
               end repeat
            end tell
         end tell
      end tell
   end tell
end open_settings_to
--------------------------------------------------------

Look for: --» •• ccstone •• to see where I placed the run code.

As I understand it, the question is posed incorrectly. Because GUI scripting (clicks) can’t work with a hidden GUI. Deciphering: GUI scripting means Scripting the Graphical User Interface (that is, the window on the front) .

That is, the question should be put like this: I need a solution that performs the same task, but without GUI scripting (click / click, etc.)

1 Like

Hi,

Thanks for the help. Again, I’m a layman so coding terminology is going right over my head for the most part. The script was written by someone on reddit. If I’m following you correctly, it was not well written and you improved upon it, is that correct?

Ah, that makes sense. Not as simple as I thought. I guess the new question indeed is: is there a way to achieve the same results other than with UI scripting?

Here is the problem I’m running into:

I do have a shell script that toggles the spellchecker language.

# Check the current language setting
currentLang=$(defaults read -g NSPreferredSpellServerLanguage)

# Toggle between English and French
if [ "$currentLang" == "en" ]; then
    defaults write -g NSPreferredSpellServerLanguage fr
    echo "Spelling language set to French"
    osascript -e 'display notification "Spelling language set to French" with title "Keyboard Settings"'

else
    defaults write -g NSPreferredSpellServerLanguage en
    echo "Spelling language set to English"
    osascript -e 'display notification "Spelling language set to English" with title "Keyboard Settings"'
fi

Unfortunately, that script requires apps already running to be restarted for the change to take effect. Problem is, I often only remember to change the spellchecker’s language until after I’ve started working on a new document.

Changing the spellchecker’s language in System Settings (either manually or through UI scripting) solves that problem, but I’d like to find a scripting solution that doesn’t require the UI to be displayed. I’m not even sure that is possible.

It’s not badly written per se, but I don’t entirely like the way it’s constructed.

I tried your script, but unless I’m doing something wrong (very possible), nothing happens. I Shortcuts is showing it’s initiated it as an AppleScript in the Shortcuts app. The scripts starts but no command is executed. It doesn’t stop either. I copied it as is.

@80sTherapy,

Looking again at the original script in your request, I see that you need code to switch between 2 keyboard sources - English and French.

First set manually Spelling: Automatic By Language in your keyboard settings.

With this setup, the task of switching Spelling Language is reduced to the less complex task of switching Keyboard Language. Then, the following simple solution is enough for you. It switches the two recent used languages in the keyboard. (Of course, you can quickly switch the keyboard language manually in the extra menu without using a script at all.)

If the list of sources for your keyboard is limited to only these two sources, then this is an even more compelling reason for using this simple solution.
.

tell application "System Events" to key code 49 using control down

.
NOTES:

  1. Another, more complex solution is to use the Carbon framework and build a command line tool. In addition, you will need to install Xcode Command Line Tools.
  2. You can download and install read-made third-party input selector tool.

@KniazidisR

Thank you for your input. I knew of the control + space bar shortcut to change the keyboard input source. The issue here is that I am looking to change the spellchecker’s language without modifying the keyboard layout.

Accented characters are easily accessible via long presses, and my muscle memory so used to the US QWERTY keyboard, changing the layout just results in a big loss of productivity. You have to remember what the differences are or open they keybiarc visualizer, which is very suboptimal. I’m not quite sure why users who primarily use a specific keyboard layout would want that. It’s a different story on devices like the iPad where the actual keyboard changes.

At any rate, that’s the problem I’m facing. Apple makes it easy to toggle the input source and keyboard layout, but offers no practical solution for just changing the spellchecker. The spelling language can be toggled via shell scripting, but the change doesn’t take effect in apps already running, which is not practical as I often have to switch languages after having started to work on a document. The spelling language can also be toggled via AppleScript and UI scripting, but it it is rather slow to execute and displays the GUI/clicks. I’d like a solution that executes faster and in the background like the shell script does (but that makes the change take effect in apps already running). That’s the problem I’m running into, and I’m not even sure there’s a solution for it.

I thought of a solution where I could use the existing shell script but add to it so that any app that uses spellchecking that is already running saves the currently opened document, quits, restarts and reopens the document just saved, but on top of being a pretty inelegant solution, it also seems like a complicated one to implement (plus I have next to zero coding knowledge).

You didn’t mention that you were using Shortcuts to run the script…

Unfortunately I can’t test this, since I’m still stuck using Mojave – but I wouldn’t be surprised if Shortcuts wants that run handler.

I also tried running it in Script Editor with the same results.