Running Main Thread Method and Keyboard Navigating


(scriptingmd) #1

In my attempts to run Shane Stanley’s August 2015 DatePicker applescript on Script Debugger, I could run that applescript on a main thread in Script Debugger with the command:

my performSelectorOnMainThread:withObject:waitUntilDone:

That command allowed the DatePicker alert pane to display.

What methods might exist to navigate from a keyboard, rather than from a mouse?


(Shane Stanley) #2

Yes, that’s the way to run code on the main thread in Script Debugger.

It would be a standard dialog, so anything that works elsewhere. I can’t recall the script you’re referring to, so I can’t check.


(scriptingmd) #3

The Date Picker script, that I was referencing, included the following. I added one line to call performSelectorOnMainThread, based upon instructions listed at https://latenightsw.com/applescriptobjc-in-script-debugger-6/

-- Created 2015-08-20 by Shane Stanley
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use script "BridgePlus"

-- create a view
set theView to current application's NSView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, 100, 200))

-- create date picker
set datePicker to current application's NSDatePicker's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, 100, 100))

-- set style: choices are NSTextFieldAndStepperDatePickerStyle, NSClockAndCalendarDatePickerStyle, or NSTextFieldDatePickerStyle
datePicker's setDatePickerStyle:(current application's NSClockAndCalendarDatePickerStyle)

-- set elements: choices include NSHourMinuteDatePickerElementFlag, NSHourMinuteSecondDatePickerElementFlag, NSTimeZoneDatePickerElementFlag, NSYearMonthDatePickerElementFlag, and NSEraDatePickerElementFlag
datePicker's setDatePickerElements:((current application's NSYearMonthDayDatePickerElementFlag) + (current application's NSHourMinuteSecondDatePickerElementFlag as integer))


-- set initial date
datePicker's setDateValue:(current application's NSDate's |date|())

-- get the size it needs
set theSize to datePicker's fittingSize()

--resize the picker and view accordingly
theView's setFrameSize:theSize
datePicker's setFrameSize:theSize

-- add the picker to the view
theView's setSubviews:{datePicker}

-- create an alert
set theAlert to current application's NSAlert's alloc()'s init()

-- set up alert
tell theAlert
	its setMessageText:"Pick a date"
	its setInformativeText:"Any date"
	its addButtonWithTitle:"OK"
	its addButtonWithTitle:"Cancel"
	its setAccessoryView:theView
end tell

--  this line was added to bring the displayAlert function on the main thread.
set returnCode to my performSelectorOnMainThread:"displayAlert:" withObject:theAlert waitUntilDone:true

-- show alert in modal loop
on displayAlert:theAlert
	set returnCode to theAlert's runModal()
	if returnCode = (current application's NSAlertSecondButtonReturn) then error number -128
end displayAlert:

-- retrieve date
set theDate to ASify from (datePicker's dateValue()) -- or simply coerce to date in 10.11

(Shane Stanley) #4

That’s still doing some resizing that should be done on the main thread. Here it is with that fixed, plus simplified a little:

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use script "BridgePlus"

-- create a view
set theView to current application's NSView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, 100, 200))

-- create date picker
set datePicker to current application's NSDatePicker's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, 100, 100))

-- set style: choices are NSTextFieldAndStepperDatePickerStyle, NSClockAndCalendarDatePickerStyle, or NSTextFieldDatePickerStyle
datePicker's setDatePickerStyle:(current application's NSClockAndCalendarDatePickerStyle)

-- set elements: choices include NSHourMinuteDatePickerElementFlag, NSHourMinuteSecondDatePickerElementFlag, NSTimeZoneDatePickerElementFlag, NSYearMonthDatePickerElementFlag, and NSEraDatePickerElementFlag
datePicker's setDatePickerElements:((current application's NSYearMonthDayDatePickerElementFlag) + (current application's NSHourMinuteSecondDatePickerElementFlag as integer))


-- set initial date
datePicker's setDateValue:(current application's NSDate's |date|())

----resize the picker 
my performSelectorOnMainThread:"resizePicker:" withObject:datePicker waitUntilDone:true
-- create an alert
set theAlert to current application's NSAlert's alloc()'s init()

-- set up alert
tell theAlert
	its setMessageText:"Pick a date"
	its setInformativeText:"Any date"
	its addButtonWithTitle:"OK"
	its addButtonWithTitle:"Cancel"
	its setAccessoryView:datePicker
end tell

--  this line was added to bring the displayAlert function on the main thread.
set returnCode to my performSelectorOnMainThread:"displayAlert:" withObject:theAlert waitUntilDone:true

-- retrieve date
set theDate to ASify from (datePicker's dateValue()) -- or simply coerce to date in 10.11

on resizePicker:datePicker
	---- get the size it needs
	set theSize to datePicker's fittingSize()
	----resize the picker 
	datePicker's setFrameSize:theSize
end resizePicker:

-- show alert in modal loop
on displayAlert:theAlert
	set returnCode to theAlert's runModal()
	if returnCode = (current application's NSAlertSecondButtonReturn) then error number -128
end displayAlert:

(scriptingmd) #5

Shane’s script works very well for displaying a date picker, and is a welcome solution to this problem. The script successfully returns a date, if the default OK button or return is keyed. The script, however, also returns the same, if the Cancel button is clicked.
The line

set returnCode to my performSelectorOnMainThread:"displayAlert:" withObject:theAlert waitUntilDone:true

from my attempts, failed to return any value. It appears that in the script

performSelectorOnMainThread

returns no value.
I am also unsure as to the reason, it is modified by the term

my

as the function appears to be an Objective C function
The function

displayAlert

does not appear to be called in the above script, if that is the missing solution.

The script returns a date regardless of the manner in which the alert dialog is dismissed.
I would appreciate any help in clarifying the above, and in changing the script to return both a successful date, as it already does, as well as an error code or value if the Cancel button is clicked.
In other words, what modifications to the script will return a value other than the date, if the Cancel button is clicked?
Thanks


(Shane Stanley) #6

The performSelector… methods don’t return a value. The way around it is to have the handler you perform put the result you want into a script property, which you can then read when the handler is done.


(scriptingmd) #7

Using your suggestion, Shane, I looked at the example that you wrote in Script Debugger 6, tutorials & how-tos regarding ApplescriptObjc.
To further understand your reply. With your tutorial, to the above script,
I added a script property

property returnCode : missing value

Later in the script, following the line

my performSelectorOnMainThread:"displayAlert:" withObject:theAlert waitUntilDone:true

I added the command

returnCode

Those additions succeeded in returning 1000 for the default button and 1001 for the second button, when one of those buttons was clicked.

When, however, I substituted the command

set returnCode to my performSelectorOnMainThread:"displayAlert:" withObject:theAlert waitUntilDone:true

AppleScript execution raised an error with

The variable returnCode is not defined.

I have searched for a meaning to the property

returnCode

but could not find an Applescript reference to it, outside of your example, on latenightsw.com web page, referenced above.
Perplexed, I have two questions, that I hope you can answer.

One, does the property

returnCode

have a special meaning?

Two, why does the returnCode receive the value of the button pressed in the alert dialog when returnCode follows in the next line of code, but does not receive that value when it is assigned a value via the set command, as in the following line?

set returnCode to my performSelectorOnMainThread:"displayAlert:" withObject:theAlert waitUntilDone:true

Thank your for your great help, tutorials, and insights.


(Shane Stanley) #8

As I said, that method doesn’t return a result. It’s like saying:

set returnCode to beep

There is no result to the beep command, so the variable is not defined. The variable name is irrelevant.