Sunday, November 27, 2022

#1 2021-05-28 02:35:13 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1083

Error message from performSelectorOnMainThread

My approach to make AppleScriptObjC code to be running in main thread is:

Applescript:

if current application's NSThread's isMainThread() as boolean then
   my performHandlerName:params
else
   my performSelectorOnMainThread:"performHandlerName:" withObject:params waitUntilDone:true
end if

ex.

Applescript:

use framework "Foundation"

property params : missing value

if current application's NSThread's isMainThread() as boolean then
   my performHandlerName:params
   log "mainThread..."
else
   my performSelectorOnMainThread:"performHandlerName:" withObject:params waitUntilDone:true
   log "performSelectorOnMainThread..."
end if

on performHandlerName:params
   -- let make a error
   get info of me
end performHandlerName:

-- run the same script with ctrl + command + r and error message is: can't get info.

If I run the script with my handlers It doesn't always return error if something is wrong.
In other words if performSelectorOnMainThread... method is using I do not see error message.

If I do the same thing by ctrl + command + r it will return error message.

So my question is:
How could I get error message from performSelectorOnMainThread... method to work ??

Do I need to include something else beside... or is the error hidden from the main Script Editor.

Its little confusing... why this behavior...

Last edited by Fredrik71 (2021-05-28 02:44:45 am)


Node-RED makes it easy to automate IoT

Offline

 

#2 2021-05-28 06:02:47 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6797

Re: Error message from performSelectorOnMainThread

The performSelectorOnMainThread::: method doesn't return a result. You need to use properties to pass values back.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#3 2021-05-28 06:50:12 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1083

Re: Error message from performSelectorOnMainThread

@Shane
You mean in other words I need to ask for info from NSAppleEventDescriptor in ASObjC
Its sounds little to complicated...

How do you handling it in Script Debugger I believe it doesn't have run in main thread. ??

Its so easy to do something wrong and the application will crash, we need a more safe heaven smile


Node-RED makes it easy to automate IoT

Offline

 

#4 2021-05-28 06:51:22 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2583

Re: Error message from performSelectorOnMainThread

Shane Stanley wrote:

The performSelectorOnMainThread::: method doesn't return a result. You need to use properties to pass values back.


Thank once again, Shane. Something like this, or something simpler?

Applescript:


use framework "Foundation"
property errorDescription : ""
property params : missing value
global handlerResult

if current application's NSThread's isMainThread() as boolean then
   my performHandlerName:params
   log "mainThread..."
else
   my performSelectorOnMainThread:"performHandlerName:" withObject:params waitUntilDone:true
   if errorDescription is not "" then error errorDescription
   log "performSelectorOnMainThread..."
   handlerResult
end if

on performHandlerName:params
   -- let make a error
   try
       set handlerResult to get info of me
   on error A
       set errorDescription to A
   end try
end performHandlerName:

Last edited by KniazidisR (2021-05-28 07:14:06 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 15.6.1
Ram: 4 GB

Offline

 

#5 2021-05-28 07:19:00 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1083

Re: Error message from performSelectorOnMainThread

@KniazidisR, Thanks.

I made little change in your code...

Applescript:


use framework "Foundation"
use scripting additions

property errorDescription : false
property params : missing value

if current application's NSThread's isMainThread() as boolean then
   my performHandlerName:params
   log "mainThread..."
else
   my performSelectorOnMainThread:"performHandlerName:" withObject:params waitUntilDone:true
   log "performSelectorOnMainThread..."
end if

-- Error handler
if errorDescription is false then
   result
else
   errorDescription
end if

on performHandlerName:params
   -- let make a error
   try
       get info of me
   on error error_message number error_number
       set errorDescription to error_message & " with error number " & error_number
   end try
end performHandlerName:

Last edited by Fredrik71 (2021-05-28 11:51:32 am)


Node-RED makes it easy to automate IoT

Offline

 

#6 2021-05-28 08:54:10 am

Mark FX
Member
From:: UK
Registered: 2011-08-12
Posts: 166

Re: Error message from performSelectorOnMainThread

Don't forget that when you refer to "current application", from a script open in "Script Editor", the "current application" is pointing to the Script Editor application.

the only time "current application" refers to the AppleScript application you might be working on, is when the AppleScript application itself is being run on its own.
Or when working on an AppleScript project in Xcode.

if you post the code below into a script in "Script Editor", you will see that the "current application is not referring to your script, but the Script Editor application".

Applescript:


on run
   set theApp to current application
   set appName to name of theApp
   display alert appName
   return
end run

As for other script editors, I can't tell you what they might return, but I suspect the same result.
Most of the time this doesn't matter when using "Cocoa Framework" classes, but when checking Thread operations, you have to be clear what application you referring too.

I hope that's useful info when using "current application".

By default, AppleScript code will run on the main thread, and you don't have to specify that it's run on that thread.
The same is true for UI code, it has to be run on the main thread, and can't be run on a background thread without raising an error.

Applescript:


my performSelectorOnMainThread:"performHandlerName:" withObject:params waitUntilDone:true
-- OR
my performHandlerName:params

So the above code is going to perform exactly the same thing, and there's really no need for the first example when using AppleScript.

Regards Mark

Last edited by Mark FX (2021-05-28 09:46:54 am)

Offline

 

#7 2021-05-28 09:47:44 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2583

Re: Error message from performSelectorOnMainThread

Fredrik71 wrote:

@KniazidisR, Thanks.

I made little change in your code...


Fredrik71,

1) As Shane sad, the performSelectorOnMainThread method doesn't return AppleScript's special variable result. Log command doesn't return AppleScript's result as well. So, you should fill custom theResult variable inside the handler.

2) errorDescription initially should be false (no error).

3) Mark say "By default, AppleScript code will run on the main thread, and you don't have to specify that it's run on that thread." But you can see yourself this isn't true when the code is executed as script. You can of course, remove all this performSelectorOnMainThread stuff before save script as application.

Applescript:


use framework "Foundation"
use scripting additions

property errorDescription : false -- EDITED
property params : missing value
global theResult -- ADDED

if current application's NSThread's isMainThread() as boolean then
   my performHandlerName:params
   log "mainThread..."
else
   my performSelectorOnMainThread:"performHandlerName:" withObject:params waitUntilDone:true
   log "performSelectorOnMainThread..."
end if

-- Error handler
if errorDescription is false then
   theResult -- EDITED
else
   errorDescription
end if

on performHandlerName:params
   -- let make a error
   try
       set theResult to get info of me -- EDITED
   on error error_message number error_number
       set errorDescription to error_message & " with error number " & error_number
   end try
end performHandlerName:

Last edited by KniazidisR (2021-05-28 10:14:34 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 15.6.1
Ram: 4 GB

Offline

 

#8 2021-05-28 10:11:50 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1083

Re: Error message from performSelectorOnMainThread

Here is example that work for me... both as run script and ctrl + command + r in Script Editor.

I use:

if errorDescription is not false then
    errorDescription
end if


Applescript:

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

property arguments : missing value
property errorDescription : false

on run
   set arguments to {title:"Title"}
   if current application's NSThread's isMainThread() as boolean then
       my performSplitView:arguments
   else
       my performSelectorOnMainThread:"performSplitView:" withObject:arguments waitUntilDone:true
   end if
   -- Error handler for performSelectorOnMainThread:withObject:waitUntilDone:
   if errorDescription is not false then
       errorDescription
   end if
end run

on performSplitView:arguments
   try
       set {theWidth, theHeight} to {1200, 800}
       
       set theButton1 to createButton(0, 0, theWidth / 2, theHeight)
       theButton1's setTitle:"Cancel"
       
       set theButton2 to createButton(0, 0, theWidth / 2, theHeight)
       theButton2's setTitle:"OK"
       
       set theSplitView to createSplitView(0, 0, theWidth, theHeight)
       theSplitView's setDividerStyle:(current application's NSSplitViewDividerStyleThin)
       theSplitView's setVertical:true
       theSplitView's addSubview:theButton1
       theSplitView's addSubview:theButton2
       
       set theWindow to createWindowWithRect(0, 0, theWidth, theHeight)
       theWindow's setTitle:(arguments's title)
       theWindow's setContentView:theSplitView
       
       set theWindowController to createWindowController(theWindow)
       theWindowController's showWindow:me
       theWindowController's |window|'s |center|()
       theWindowController's |window|'s makeKeyAndOrderFront:me
       -- Error handler for performSelectorOnMainThread:withObject:waitUntilDone:
   on error error_message number error_number
       set errorDescription to error_message & " with error number " & error_number
   end try
end performSplitView:

on createButton(xMin, yMin, xLen, yLen)
   set buttonSize to current application's NSMakeRect(xMin, yMin, xLen, yLen)
   set theButton to current application's NSButton's alloc()'s initWithFrame:buttonSize
   return theButton
end createButton

on createSplitView(xMin, yMin, xLen, yLen)
   set splitViewSize to current application's NSMakeRect(xMin, yMin, xLen, yLen)
   set theSplitView to current application's NSSplitView's alloc()'s initWithFrame:splitViewSize
   return theSplitView
end createSplitView

on createWindowWithRect(xMin, yMin, xLen, yLen)
   set windowSize to current application's NSMakeRect(xMin, yMin, xLen, yLen)
   set winStyle to (current application's NSWindowStyleMaskTitled as integer) + (current application's NSWindowStyleMaskClosable as integer)
   set theWindow to current application's NSWindow's alloc()'s initWithContentRect:windowSize styleMask:winStyle backing:2 defer:yes
   return theWindow
end createWindowWithRect

on createWindowController(theWindow)
   set theWindowController to current application's NSWindowController's alloc()'s initWithWindow:theWindow
   return theWindowController
end createWindowController

Last edited by Fredrik71 (2021-05-28 11:50:55 am)


Node-RED makes it easy to automate IoT

Offline

 

#9 2021-05-28 10:37:15 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1083

Re: Error message from performSelectorOnMainThread

Why I start this topic was to get the error message by running a script in Script Editor.
When a script is finish I do not need the error checking... and properly would remove it.

The approach was to get error message for performSelectorOnMainThread method.

And I find a solution that work when I code... @KniazidisR, thanks.

Most of my script do not use error checking because I think I do not need it if a script do work.
And most of the time AppleScripts do not have thousand of line of code so I do not see the
reason to always use error checking. If I do not know or have forgot what my script does
I properly would ask myself why do I run it in the first place.

I do comment my AppleScript so its very easy for me to understand it.

Its only my approach to do things. smile


Node-RED makes it easy to automate IoT

Offline

 

#10 2021-05-28 11:20:26 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2583

Re: Error message from performSelectorOnMainThread

That is good, but errorDescription in your posts #5 and #8 initially should be false:

Applescript:


property errorDescription:false

Last edited by KniazidisR (2021-05-28 11:23:04 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 15.6.1
Ram: 4 GB

Offline

 

#11 2021-05-28 11:23:16 am

Fredrik71
Member
Registered: 2019-10-23
Posts: 1083

Re: Error message from performSelectorOnMainThread

@Mark FX, I do not say you are wrong but my experience from other people code that use
the method performSelectorOnMainThread::: have made Script Editor to crash. And the
solution was to use if statement like the one I use.

But I will do some more testing on my code so I could see for myself wink...
I know you have great knowledge and its not to respect you.
I'm also new to Xcode and start doing something only for a few weeks ago.

If I (Run the Script) in Script Editor it use performSelectorOnMainThread method...
If run with ctrl + command + r (Run in Foreground) its running in main Thread.

So I'm not sure...

Do you mean like this...

Applescript:

if not my NSThread's isMainThread() as boolean then
       my performSelectorOnMainThread:"performSplitView:" withObject:arguments waitUntilDone:true
   else
       my performSplitView:arguments
   end if

Last edited by Fredrik71 (2021-05-28 11:43:59 am)


Node-RED makes it easy to automate IoT

Offline

 

#12 2021-05-28 12:56:05 pm

Mark FX
Member
From:: UK
Registered: 2011-08-12
Posts: 166

Re: Error message from performSelectorOnMainThread

Hi Fredrik71

If I run code in Script Editor application, I will get a different result to the same code run in Xcode.

In Script Editor

Applescript:


property myApp : a reference to current application

property params : missing value

if myApp's NSThread's isMainThread() as boolean then
   my performHandlerName:params
   log "mainThread..."
else
   my performSelectorOnMainThread:"performHandlerName:" withObject:params waitUntilDone:true
   log "performSelectorOnMainThread..."
end if

The 'else' clause will win here, because the "current application" will be referring to the Script Editor application, which may well be using multiple threads in it's normal operations.

in Xcode

Applescript:


property app : a reference to current application

on applicationDidFinishLaunching:notification
   if my app's NSThread's isMainThread() as boolean then
       my displayPreferencesWindow:me
       log "displayPreferencesWindow:"
   else
       my performSelectorOnMainThread:"displayPreferencesWindow:" withObject:me waitUntilDone:true
       log "performSelectorOnMainThread:"
   end if
end applicationDidFinishLaunching:

The 'if' clause wins here, because the "current application" will be referring to my AppleScriptObjC application object.

So you may well get different results, depending on where your code is running.

It's worth considering the facts about AppleScript, it's not a multithreading programming language, and can only run one line of code at a time, and cannot run code asynchronously, it has only one instance of itself, and cannot runs different code sources simultaneously.
Because of these limitations, it is mostly pointless trying to work on different threads with AppleScript code.

The only time I would consider using a different thread with AppleScript code, is to keep a window's UI responding to events, while running time consuming code in the background, but this has to be done carefully, and is not really advisable.

See this.
https://macscripter.net/viewtopic.php?id=48400

The various "NSObject performSelector" functions where designed with ObjectiveC programmers in mind, they where never intended for AppleScript code, although with the opening up the Cocoa Frameworks to AppleScript with the AppleScriptObjC Framework, it give us access to the threading functions, but we must not con ourselves that we can use multithreading with AppleScript, because we can't in reality, better to keep our AppleScript code clean and concise, and not to unnecessarily make our code complex when it is not required, especially for the benefit of novice scripters looking in on these forum threads.

You can obviously continue to experiment with threading AppleScript code, but because of the single threaded nature of AppleScript, it's only going to be smoke and mirrors to convince yourself that you will be gaining any real performance.

For me personaly this code wins.

Applescript:

my performHandlerName:params

Because there is no performance advantage with the "performSelector" function, and it's cleaner and more concise code.

But were all different.
And I wish you good luck with it.

Regards Mark

Last edited by Mark FX (2021-05-28 01:43:40 pm)

Offline

 

#13 2021-05-28 04:19:40 pm

Fredrik71
Member
Registered: 2019-10-23
Posts: 1083

Re: Error message from performSelectorOnMainThread

@Mark FX, thanks for sharing...

From my knowledge its not about performance or trying to gain a better performance.

The reason is or why I did choose the approach was because some code I did but also
from someone else in this forum made Script Editor to crash.
Meaning it was not possible to run the script from (Run the Script) from the toolbar. On other had it was possible to run the code on main thread (Run in Foreground). From that conclusion I made a choose to do something about it.

Here is the text from Apple Reference Guide

performSelectorOnMainThread: withObject:waitUntilDone:

Performs the specified selector on the application’s main thread during that thread’s next run loop cycle. These methods give you the option of blocking the current thread until the selector is performed.



Here is information about NSView

The NSView class is generally thread-safe, with a few exceptions. You should create, destroy, resize, move, and perform other operations on NSView objects only from the main thread of an application. Drawing from secondary threads is thread-safe as long as you bracket drawing calls with calls to lockFocusIfCanDraw and unlockFocus.
If a secondary thread of an application wants to cause portions of the view to be redrawn on the main thread, it must not do so using methods like display, setNeedsDisplay:, setNeedsDisplayInRect:, or setViewsNeedDisplay:. Instead, it should send a message to the main thread or call those methods using the performSelectorOnMainThread:withObject:waitUntilDone: method instead.



The main thread of the application is responsible for handling events. The main thread is the one blocked in the run method of NSApplication, usually invoked in an application’s main function. While the Application Kit continues to work if other threads are involved in the event path, operations can occur out of sequence. For example, if two different threads are responding to key events, the keys could be received out of order. By letting the main thread process events, you achieve a more consistent user experience. Once received, events can be dispatched to secondary threads for further processing if desired.
You can call the postEvent:atStart: method of NSApplication from a secondary thread to post an event to the main thread’s event queue. Order is not guaranteed with respect to user input events, however. The main thread of the application is still responsible for handling events in the event queue.



Lets be do more testing...

Any script with UI of any kind could crash if its running in wrong environment.
The If statement could check the status of that. Before we execute the rest of the code.

[applescript]use framework "Foundation"
use scripting additions

display dialog "The main thread is: " & (my NSThread's isMainThread() as boolean)

(**
* Run the script in Editor
* Script Editor = false (Run the Script) - Toolbar
* Script Editor = true (Run in Foreground) ctrl + command + r
*
* Save the script to file
* osascript = true (scpt) (stay open is not possible)
* osascript = true (scptd) (stay open is not possible)
* osascript = true (app)
*
* Automator = true when (Run the Action) but false when (Begin execution of the workflow)

* Xcode = true the script is a applet. Build->Product->Debug->MyApplet.app
*)[/AppleScript]

Sometimes its useful to run a script in Automtor with Run AppleScript Action because it could
output more information that Script Editor do not.

ex.
Script with custom UI without performSelectorOnMainThread:::
Or
Other issues there main thread applies...

From Automator we get:
The action "Run AppleScript" encountered an error: "NSWindow drag regions should only be invalidated on the Main Thread".

If we run the same code in Script Editor more likely it will crash. (it did, I tested)

Script Editor do not have a safe approach for execute code if the environment is wrong but Automator does at least the application do not crash.

Last edited by Fredrik71 (2021-05-29 02:40:15 am)


Node-RED makes it easy to automate IoT

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)