write text on menu bar

Hello, I am new to AppleScript and I’d like to write a text output from command line to the menu bar. I find some sample from internet, but has running error. Could someone help me?

I got error “error “NSWindow drag regions should only be invalidated on the Main Thread!” number -10000” on line “set StatusItem to bar’s statusItemWithLength:-1.0”

I am on Monterey 12.0.1

https://apple.stackexchange.com/questions/244239/applescript-write-text-on-menu-bar

aero wrote:


What error is being displayed ?
Is the error displayed in the Script Editor’s log ?
Or is there an error Alert dialog box being displayed ?

Setting the “statusItemWithLength:” to a negative value of “-1.0” will make the Status Bar Item invisible, which is not an error.
you would need to set the “statusItemWithLength:” to a positive value, in order to make it visible.

Or allow it to be automatically adjusted to the assigned title or image width like this.


set statusItem to statusBar's statusItemWithLength:(current application's NSVariableStatusItemLength)

Secondly the NSStatusItem’s title() function is marked as deprecated in Apple’s documentation, although it is still available in “Mojave”, it may not still be available in “Monterey”.
So it would be better to use the NSStatusItem’s button()'s title() instead, like this.


set statusBarTitle to "Status Bar Title"
set statusItem to statusBar's statusItemWithLength:(current application's NSVariableStatusItemLength)
statusItem's button's setTitle:statusBarTitle

I don’t have access to “Monterey” for testing, so there could be differences on the later OS, but the online Apple Developer Documentation still lists these functions as valid.

Your script currently does nothing other than try to display the status bar item, and then removes this item after 5 seconds, so no one can help much without knowing what your trying to achieve.
So give more info on what your trying to do with this status bar script.

Also your giving a description for an Error with an NSWindow, but you posted code has no code relevant to an NSWindow class object, so you should post that code as well If someone is going to be able to help you.
But for the record, if your trying to run code associated with windows and other UI elements, that code has to be run on the main thread, and not in a background thread, as that’s against the operating system law.

Regards Mark

Thank you Mark for the help. I changed script to below and still got the same error in the script editor result window

“error “NSWindow drag regions should only be invalidated on the Main Thread!” number -10000” .

what I am trying to do is to display a text (better with an icon) in the system menu bar. the text is generated from a shell script for example to read the cpu temperature and fan speed and show them in the system menu bar.

for now I just want to display “HELLO” in the system menu bar as the first step.

I know there are a bunch of application to do it, but you are limited to the function of the app. If I can simply use Script to display a text in the system menu bar, it will be more flexible and not depends on third party app.

use framework "Foundation"
use framework "AppKit"
use scripting additions
property StatusItem : missing value
property newMenu : class "NSMenu"
-- myVariable derived from whatever code >>

set myVariable to "HELLO"

set bar to current application's NSStatusBar's systemStatusBar
set StatusItem to bar's statusItemWithLength:(current application's NSVariableStatusItemLength)

StatusItem's button's setTitle:myVariable

set newMenu to current application's NSMenu's alloc()'s initWithTitle:"Custom"
StatusItem's setMenu:newMenu

delay 5
current application's NSStatusBar's systemStatusBar()'s ¬
	removeStatusItem:StatusItem

Try saving it as an applet and running it like that – that error is happening because script editors run scripts off the main thread, and code that modifies the UI needs to be run on the main thread.

You can also modify the script so the relevant code is run on the main thread by putting it all in a handler and calling it with the performSelectorOnMainThread:withObject:waitUntilDone: method.

@aerolite

What you need to understand is that AppleScript code is just text, and the script file you have open in the Script Editor app is just a text file, it is not an application in itself.
So when you run AppleScriptObjC code in the Script Editor app, its not your AppleScript code that runs the commands, it’s the Script Editor app that does the actual execution.

So as Shane has pointed out, the Script Editor app runs that code in a background thread, in order to allow it’s own interface to continue to work and be responsive.
So when working with visual screen UI elements, you have to tell the Script Editor app to execute the code on the main thread, as that’s the operating system rules for UI manipulation, and not to execute the code on a background thread.
Or you have to do as Shane as also pointed out, and save your code as an applet or application, so that the Script Editor is no longer in control of the thread, and your stand alone AppleScript applet is, which will by default run any contained code on the main thread.

So with that being understood, while your working on your AppleScript code in the Script Editor app, you have to make sure that any UI manipulation that the Script Editor app does, is carried out on the main thread, and you do that by using the “performSelectorOnMainThread:withObject:waitUntilDone:” method that Shane has pointed too.
Like this example below.


use framework "Foundation"
use framework "AppKit"
use scripting additions

property myApp : a reference to current application

property statusBar : missing value
property statusItem : missing value

set statusItemTitle to "Status Bar Title"

set my statusBar to myApp's NSStatusBar's systemStatusBar()

if my NSThread's isMainThread() as boolean then
	my displayStatusItem:statusItemTitle
else
	my performSelectorOnMainThread:"displayStatusItem:" withObject:statusItemTitle waitUntilDone:true
end if

on displayStatusItem:title
	set my statusItem to statusBar's statusItemWithLength:(myApp's NSVariableStatusItemLength)
	statusItem's button's setTitle:title
	
	set statusItemMenu to myApp's NSMenu's alloc()'s initWithTitle:""
	
	set quitMenuItem to myApp's NSMenuItem's alloc()'s initWithTitle:"Quit" action:"quitStatusItem" keyEquivalent:"q"
	quitMenuItem's setTarget:me
	
	statusItemMenu's addItem:quitMenuItem
	
	statusItem's setMenu:statusItemMenu
end displayStatusItem:

on quitStatusItem()
	set quitStatusItem to button returned of (display alert "Do you want to quit the status bar item ?" buttons {"NO", "YES"})
	if quitStatusItem = "YES" then
		my performSelectorOnMainThread:"removeStatusItem" withObject:(missing value) waitUntilDone:true
	end if
end quitStatusItem

on removeStatusItem()
	statusBar's removeStatusItem:statusItem
end removeStatusItem

I hate to post code that I can not test on the correct operating system, but I don’t have access to “Monterey”, and was only able to test it on “Mojave”, so I’ll apologise in advance for any issues that my occur from the OS differences, but I’m sure someone running “Monterey” can step in and help with any OS specific solutions.

But with that being said, you can see the code check which thread it is being run on, and if it’s not the main thread, then it uses the “performSelectorOnMainThread:withObject:waitUntilDone:” method.
This code may not be what you want, but is simply to show you how to perform UI code on the main thread while in the Script Editor app, and also how to use methods and create a menu for your status item if you need to.

if you want to change the status item’s title as you describe, you also have to make sure that is also done on the main thread, perhaps via another created method called “on changeStatusItemTitle:”.

I hope this makes things a bit more understandable.

Regards Mark

Thank you Shane. it works once I save it as app.
Thank you Mark for your explanation and your script works on Monterey.

Hello Mark,

Thank you for provide the example. I tried you example in script editor and it works perfectly. as I understand it should work if I export it to as app as you use code below to see it’s mainThread or not. however it looks like the app just briefly flash in the menu bar, then disappeared (ended, I guess). Could you help more?

Thanks

if my NSThread's isMainThread() as boolean then
   my displayStatusItem:statusItemTitle
else
   my performSelectorOnMainThread:"displayStatusItem:" withObject:statusItemTitle waitUntilDone:true
end if

I also tried to remove the if and export it to as app and it’s the same.

use framework "Foundation"
use framework "AppKit"
use scripting additions

property myApp : a reference to current application

property statusBar : missing value
property statusItem : missing value

set statusItemTitle to "Status Bar Title"

set my statusBar to myApp's NSStatusBar's systemStatusBar()

my displayStatusItem:statusItemTitle

on displayStatusItem:title
	set my statusItem to statusBar's statusItemWithLength:(myApp's NSVariableStatusItemLength)
	statusItem's button's setTitle:title
	
	set statusItemMenu to myApp's NSMenu's alloc()'s initWithTitle:""
	
	set quitMenuItem to myApp's NSMenuItem's alloc()'s initWithTitle:"Quit" action:"quitStatusItem" keyEquivalent:"q"
	quitMenuItem's setTarget:me
	
	statusItemMenu's addItem:quitMenuItem
	
	statusItem's setMenu:statusItemMenu
end displayStatusItem:

on quitStatusItem()
	my removeStatusItem()
end quitStatusItem

on removeStatusItem()
	set removeStatusItem to button returned of (display alert "Do you want to remove the status bar item ?" buttons {"NO", "YES"})
	if removeStatusItem = "YES" then
		statusBar's removeStatusItem:statusItem
	end if
end removeStatusItem

@aerolite

Firstly you are correct with your last script posting, in that you can remove the main thread check and method calling, once your script is saved as an application.
But do remember that you will need that main thread code when testing in the Script Editor.

Secondly the reason that the status item only flashes up quickly, is that the code runs once from top to bottom, and then exits the script, so you need to save it as a stay open application.

So copy and paste your last script into a new script file, then change the bottom of the script to this example below, you will see that I have added the “tell me to quit” to the “on quitStatusItem()” function.
And also that I have added the “on Quit{}” function as shown below.


on quitStatusItem()
	set quitStatusItem to button returned of (display alert "Do you want to quit the status bar item ?" buttons {"NO", "YES"})
	if quitStatusItem = "YES" then
		my removeStatusItem()
		tell me to quit
	end if
end quitStatusItem


on removeStatusItem()
	statusBar's removeStatusItem:statusItem
end removeStatusItem

-- And Add This Function

on quit {}
	continue quit
end quit

Then once you have made these changes resave the file from the “File” → “Save” menu.
And in the save dialog box, select the “Application” type from the drop down box.
And then select the “Stay open after run handler” check box.

with the above changes you should have a stay open status item application.

Regards Mark

Thank you so much @Mark FX for your detailed explanation and now I have an apple scripts app running to show CPU temperature in the menubar using the script below.

now I want to improve it. you see that I have to use “statusBar’s removeStatusItem:statusItem” to remove it in the loop before I update the text or it will be multiple text in the menu bar. Is there a way to update the text without remove it? remove and re-write cause the text jump around when I have multiple similar apple scripts app running.

thx


use framework "Foundation"
use framework "AppKit"
use scripting additions

property myApp : a reference to current application

property statusBar : missing value
property statusItem : missing value

set statusItemTitle to "Hello World!"

set theREPEAT to 1
repeat while true
	
	--repeat 5 times
	set batt_level to do shell script "/Volumes/data/bin/batt_level.cmd"
	set batt_temp to do shell script "/Volumes/data/bin/batt_temp.cmd"
	set batt_volt to do shell script "/Volumes/data/bin/batt_volt.cmd"
	set cpu_temp to do shell script "/Volumes/data/bin/cpu_temp.cmd"
	set cpu_fans to do shell script "/Volumes/data/bin/cpu_fans.cmd"
	
	-- display alert "CPU" & (cpu_temp as string) & "°C"
	-- display alert cpu_fans
	if cpu_fans = "0" then
		set statusItemTitle to ("CPU " & (cpu_temp as string) & "°C")
	else
		set statusItemTitle to (("CPU " & (cpu_temp as string) & "°C FAN" & cpu_fans as string) & "R/M")
	end if
	
	
	set my statusBar to myApp's NSStatusBar's systemStatusBar()
	
	if my NSThread's isMainThread() as boolean then
		my displayStatusItem:statusItemTitle
	else
		my performSelectorOnMainThread:"displayStatusItem:" withObject:statusItemTitle waitUntilDone:true
	end if
	
	delay 3
	statusBar's removeStatusItem:statusItem
end repeat

on displayStatusItem:title
	set my statusItem to statusBar's statusItemWithLength:(myApp's NSVariableStatusItemLength)
	statusItem's button's setTitle:title
	
	set statusItemMenu to myApp's NSMenu's alloc()'s initWithTitle:""
	
	set quitMenuItem to myApp's NSMenuItem's alloc()'s initWithTitle:"Quit" action:"quitStatusItem" keyEquivalent:"q"
	quitMenuItem's setTarget:me
	
	statusItemMenu's addItem:quitMenuItem
	
	statusItem's setMenu:statusItemMenu
end displayStatusItem:

on quitStatusItem()
	set quitStatusItem to button returned of (display alert "Do you want to quit the status bar item ?" buttons {"NO", "YES"})
	if quitStatusItem = "YES" then
		my removeStatusItem()
		tell me to quit
	end if
end quitStatusItem


on removeStatusItem()
	statusBar's removeStatusItem:statusItem
end removeStatusItem

-- And Add This Function

on quit {}
	continue quit
end quit

@aerolite

Your CPU sent me an email, which said HELP!

So on behalf of your CPU, could you please stop with that crazy repeat loop.
That loop is running 5 shell scripts, and redisplaying the status bar item, and also rebuilding the status item menu, over and over forever without a break.
And it was all going so well up to this point.

Firstly you do not need to remove and redisplay the status bar item, and also rebuild the status item menu repeatedly, you only needed to do it once.
your CPU usage must be off the charts.

The first half of the script was done well in your previous posting for the moment at least.


use framework "Foundation"
use framework "AppKit"
use scripting additions

property myApp : a reference to current application

property statusBar : missing value
property statusItem : missing value

set statusItemTitle to "Status Bar Title"

set my statusBar to myApp's NSStatusBar's systemStatusBar()

if my NSThread's isMainThread() as boolean then
	my displayStatusItem:statusItemTitle
else
	my performSelectorOnMainThread:"displayStatusItem:" withObject:statusItemTitle waitUntilDone:true
end if

Done!

The bottom half of the script was also fine, and does not need any changes for the moment.


on displayStatusItem:title
	set my statusItem to statusBar's statusItemWithLength:(myApp's NSVariableStatusItemLength)
	statusItem's button's setTitle:title
	
	set statusItemMenu to myApp's NSMenu's alloc()'s initWithTitle:""
	
	set quitMenuItem to myApp's NSMenuItem's alloc()'s initWithTitle:"Quit" action:"quitStatusItem" keyEquivalent:"q"
	quitMenuItem's setTarget:me
	
	statusItemMenu's addItem:quitMenuItem
	
	statusItem's setMenu:statusItemMenu
end displayStatusItem:

on quitStatusItem()
	set quitStatusItem to button returned of (display alert "Do you want to quit the status bar item ?" buttons {"NO", "YES"})
	if quitStatusItem = "YES" then
		my performSelectorOnMainThread:"removeStatusItem" withObject:(missing value) waitUntilDone:true
		tell me to quit
	end if
end quitStatusItem

on removeStatusItem()
	statusBar's removeStatusItem:statusItem
end removeStatusItem

on quit {}
	continue quit
end quit

Done!

The only part that you now need to focus on is the middle of the script, changing the status item’s button’s title, and you don’t have to continually remove or put back the status item to do that.
So we only have to post the code for the middle part of the script at this point.

To change the status item’s button’s title we only have to use this code.


statusItem's button's setTitle:newTitle

So a new function or method, could be called from within your repeat loop, a function something like this.


repeat
	set statusItemTitle to (time string of (current date))
	my changeStatusItemTitle:statusItemTitle
	delay (1.0)
end repeat

on changeStatusItemTitle:title
	statusItem's button's setTitle:title
end changeStatusItemTitle:

I’m only guessing at this point, but are the five shell script calls to command line tools on an external drive ?
or something else ?

Please explain to me and others a bit more about these do shell script calls.
Can they be done one at a time, instead of all at once ?
Because you need to make your little status bar application, less of a system resources hog.

Regards Mark

Thank you so much @Mark FX. You are so nice and helpful. I modified the script and it works perfectly. for this app it only needs two of the shell scripts, the others are for separate apps. My shell scripts are very simple one line scripts using the build-in macOS command on other partition in my Mac.

I wonder if it’s possible to show a little icon on the menu bar. now my apps shows “CPU 52.13°C”. it will be better if I can change the text “CPU” to a little icon. I have the icns file ready.

Thanks again.

cat cpu_fans.cmd
#!/bin/zsh
sudo powermetrics --samplers smc -i1 -n1 | grep Fan | awk ‘{print $2}’

cat cpu_temp.cmd
#!/bin/zsh
sudo powermetrics --samplers smc -i1 -n1 | grep temp | awk ‘{print $4}’


use framework "Foundation"
use framework "AppKit"
use scripting additions

property myApp : a reference to current application

property statusBar : missing value
property statusItem : missing value

set statusItemTitle to "Hello World!"

set theREPEAT to 1

--repeat 5 times
set batt_level to do shell script "/Volumes/data/bin/batt_level.cmd"
set batt_temp to do shell script "/Volumes/data/bin/batt_temp.cmd"
set batt_volt to do shell script "/Volumes/data/bin/batt_volt.cmd"
set cpu_temp to do shell script "/Volumes/data/bin/cpu_temp.cmd"
set cpu_fans to do shell script "/Volumes/data/bin/cpu_fans.cmd"

-- display alert "CPU" & (cpu_temp as string) & "°C"
-- display alert cpu_fans
if cpu_fans = "0" then
	set statusItemTitle to ("CPU " & (cpu_temp as string) & "°C")
else
	set statusItemTitle to (("CPU " & (cpu_temp as string) & "°C FAN" & cpu_fans as string) & "RPM")
end if


set my statusBar to myApp's NSStatusBar's systemStatusBar()

if my NSThread's isMainThread() as boolean then
	my displayStatusItem:statusItemTitle
else
	my performSelectorOnMainThread:"displayStatusItem:" withObject:statusItemTitle waitUntilDone:true
end if

delay 3

repeat
	set cpu_temp to do shell script "/Volumes/data/bin/cpu_temp.cmd"
	set cpu_fans to do shell script "/Volumes/data/bin/cpu_fans.cmd"
	if cpu_fans = "0" then
		set statusItemTitle to ("CPU " & (cpu_temp as string) & "°C")
	else
		set statusItemTitle to (("CPU " & (cpu_temp as string) & "°C FAN" & cpu_fans as string) & "RPM")
	end if
	-- set statusItemTitle to (time string of (current date))
	my changeStatusItemTitle:statusItemTitle
	delay (1.0)
end repeat

on changeStatusItemTitle:title
	statusItem's button's setTitle:title
end changeStatusItemTitle:

on displayStatusItem:title
	set my statusItem to statusBar's statusItemWithLength:(myApp's NSVariableStatusItemLength)
	statusItem's button's setTitle:title
	
	set statusItemMenu to myApp's NSMenu's alloc()'s initWithTitle:""
	
	set quitMenuItem to myApp's NSMenuItem's alloc()'s initWithTitle:"Quit" action:"quitStatusItem" keyEquivalent:"q"
	quitMenuItem's setTarget:me
	
	statusItemMenu's addItem:quitMenuItem
	
	statusItem's setMenu:statusItemMenu
end displayStatusItem:

on quitStatusItem()
	set quitStatusItem to button returned of (display alert "Do you want to quit the status bar item ?" buttons {"NO", "YES"})
	if quitStatusItem = "YES" then
		my removeStatusItem()
		tell me to quit
	end if
end quitStatusItem


on removeStatusItem()
	statusBar's removeStatusItem:statusItem
end removeStatusItem

-- And Add This Function

on quit {}
	continue quit
end quit

That’s looking a bit more efficient, but I think you should increase the delay in the repeat loop to say 5.0 or 10.0, because you don’t really need to check the CPU temperature every second, and your app is still a bit greedy with the CPU usage, but that’s your choice.


delay(5.0)

Your .icns file is no good for the status item’s image, but you could use it for your saved AppleScript application’s icon.
Find your saved application file in a Finder window, then right click or cmd click the application file.
Select “Show Package Contents” from the pull down menu, then double click the “Contents” folder.
Then double click the “Resources” folder, then inside the that folder you should see the “applet.icns” file, so you can simply rename your custom .icns file with that same name, and drag it in the the folder to replace the original icon file.

As for displaying an image next to the status item’s title, you have three options, from easy to more difficult, and the more difficult options may require help from someone with “Monetary” OS, because the code I could post for the more difficult options, will work fine on my “Mojave”, but will probably not work on “Monterey”.

The first and easiest option is to use an emoji or symbol.
Go to the “Edit” menu in the Script Editor app, down at the bottom you will see the emoji menu item.
have a browse through the installed emoji’s and pictographs, and see if there is a picture you like.
Then you simply do this in your “changeStatusItemTitle:” function, like this.


on changeStatusItemTitle:title
	statusItem's button's setTitle:("⚙️" & title)
end changeStatusItemTitle:

The second option is to use one of the installed system images, which may be different on your OS, to the ones on my system, but you can find them here.
https://developer.apple.com/documentation/appkit/nsimagename?language=objc

you would change the top of your script to something like this.


use framework "Foundation"
use framework "AppKit"
use scripting additions

property myApp : a reference to current application

property statusBar : missing value
property statusItem : missing value
property statusItemImage : missing value

set statusItemTitle to "Status Bar Title"

set my statusBar to myApp's NSStatusBar's systemStatusBar()

set my statusItemImage to myApp's NSImage's imageNamed:(myApp's NSImageNameAdvanced)
set statusImageSize to (statusBar's thickness()) - 4
statusItemImage's setSize:(myApp's NSMakeSize(statusImageSize, statusImageSize))

And you would change the top of the “displayStatusItem:” function to something like this.


on displayStatusItem:title
	set my statusItem to statusBar's statusItemWithLength:(myApp's NSVariableStatusItemLength)
	statusItem's button's setTitle:title
	statusItem's button's setImage:statusItemImage
	statusItem's button's setImagePosition:(myApp's NSImageLeft)


So have a testing session with these first two options, and see if any of the ready made system pictures are what your looking for.
Otherwise create a png image file that you want to use, and I will show you how to load it into your application, although that might be the point where the OS differences might cause problems.
also it will be difficult to test in the Script Editor app.

if you do create your own image file, make it very small, as the height of the Status bar on my system is only 22 pixels in height, and yours may be different, but it will still be small in height.

You can get the height of your status bar with this code.


log (statusBar's thickness())

So your created image file should be 2 to 4 pixels less than you status bar height.

Or if you intend giving your app to others, who may have different screen sizes and resolutions.
Then make your image about 24 pixels in height, and it will be resized with the code I posted above.

Regards Mark

Thank you again @Mark FX. I tried both options.

the first option works perfect, but the second option doesn’t. I think it works once as I saw the gear like image appears on one of the run. I tried to run it both in script Editor and app. My status bar is also 22.

I saw the changeStatusItemTitle is called in the repeat. who called displayStatusItem, which add the image to the title.

I also tried to move the two lines below to changeStatusItemTitle and it works. should I?
statusItem’s button’s setImage:statusItemImage
statusItem’s button’s setImagePosition:(myApp’s NSImageLeft)

By the way I did change the app icon by icns file successfully and have png file ready.

thx

Here is my code.


use framework "Foundation"
use framework "AppKit"
use scripting additions

property myApp : a reference to current application

property statusBar : missing value
property statusItem : missing value
property statusItemImage : missing value

set statusItemTitle to "Hello World! ⛵️"


set my statusBar to myApp's NSStatusBar's systemStatusBar()

if my NSThread's isMainThread() as boolean then
	my displayStatusItem:statusItemTitle
else
	my performSelectorOnMainThread:"displayStatusItem:" withObject:statusItemTitle waitUntilDone:true
end if


set my statusItemImage to myApp's NSImage's imageNamed:(myApp's NSImageNameAdvanced)
set statusImageSize to (statusBar's thickness()) - 4
statusItemImage's setSize:(myApp's NSMakeSize(statusImageSize, statusImageSize))

my changeStatusItemTitle:statusItemTitle
delay 3

-- display alert (statusBar's thickness())


repeat
	set statusItemTitle to (time string of (current date))
	my changeStatusItemTitle:statusItemTitle
	delay (1.0)
end repeat

on changeStatusItemTitle:title
	statusItem's button's setTitle:(title)
end changeStatusItemTitle:

on displayStatusItem:title
	set my statusItem to statusBar's statusItemWithLength:(myApp's NSVariableStatusItemLength)
	statusItem's button's setTitle:title
	statusItem's button's setImage:statusItemImage
	statusItem's button's setImagePosition:(myApp's NSImageLeft)
	
	set statusItemMenu to myApp's NSMenu's alloc()'s initWithTitle:""
	
	set quitMenuItem to myApp's NSMenuItem's alloc()'s initWithTitle:"Quit" action:"quitStatusItem" keyEquivalent:"q"
	quitMenuItem's setTarget:me
	
	statusItemMenu's addItem:quitMenuItem
	
	statusItem's setMenu:statusItemMenu
end displayStatusItem:

on quitStatusItem()
	set quitStatusItem to button returned of (display alert "Do you want to quit the status bar item ?" buttons {"NO", "YES"})
	if quitStatusItem = "YES" then
		my removeStatusItem()
		tell me to quit
	end if
end quitStatusItem


on removeStatusItem()
	statusBar's removeStatusItem:statusItem
end removeStatusItem

-- And Add This Function

on quit {}
	continue quit
end quit

Hello Mark,

I was looking at the link you provided “https://developer.apple.com/documentation/appkit/nsimagename?language=objc” and found the symbol image SF Symbols (https://developer.apple.com/design/resources/). Is it easy than load a image?

thx

I’m not sure what your saying now, are you saying the second example is now working ?

Because if it’s working here on my “Mojave”, but not on your “Monterey”, then there would be no point in me showing the third option, as it works the same way as the previous example.
Except for the way it loads the image file.

Mark

Hello Mark,

It works once I changed to this.

thx for the help.

on changeStatusItemTitle:title
	statusItem's button's setTitle:(title)
	statusItem's button's setImage:statusItemImage
	statusItem's button's setImagePosition:(myApp's NSImageLeft)
end changeStatusItemTitle:

That sucks that you have to continually update the image.
I only had to load the image once at the top of the script, and the image stayed put.
Which is a better way to do things, so clearly “Monterey” changed something there.

So are you all good now, or did you want to load your custom image file ?

Mark

Hi Mark, like I said it did load the image once without modification, but that’s ok if we have to keep update it.

I do want to load the image if you have time to help me.

Much appreciate.

OK the last piece of the jigsaw puzzle is to load your image file into the “statusItemImage” variable.

The first thing you have to do is open up your application bundle’s folder structure again, and go to the “Resources” folder, where you updated your application icon file.
You need to put your image file in this “Resources” folder, along side the .icns file.

You may also see the “Scripts” folder in there as well, if you go into that folder you will see a AppleScript file named “main.scpt”, that’s the AppleScript file you have been working on in the Script Editor app, but it’s been renamed “main.scpt”, you can double click that script file to open it.

Then change this code.


set my statusItemImage to myApp's NSImage's imageNamed:(myApp's NSImageNameAdvanced)
set statusImageSize to (statusBar's thickness()) - 4
statusItemImage's setSize:(myApp's NSMakeSize(statusImageSize, statusImageSize))

Into this code instead.


set appBundle to myApp's NSBundle's mainBundle()
set imageFilePath to appBundle's pathForResource:"imageFileName" ofType:"png" -- Your file name
set my statusItemImage to myApp's NSImage's alloc()'s initWithContentsOfFile:imageFilePath
set statusImageSize to (statusBar's thickness()) - 4
statusItemImage's setSize:(myApp's NSMakeSize(statusImageSize, statusImageSize))

You will have to put the correct image file name in the second line down from the top.

And it’s very important not to run this code in the Script Editor app, as it will only fail and give you error messages, because the top line will load the application bundle for the Script Editor app, and will look for your image file in that bundle, instead of your own AppleScript app bundle.

So you’re going to have to save the changes, and trust that the code change works at it is intended.
If it doesn’t work, just double check you have put your image file into the correct “Resources” folder.
Or the other reason it might fail is that the code is not compatible with the “Monterey” OS.
Because I can only test on my “Mojave” system, and it works fine for me on my system.

Good Luck

Regards Mark