Reputation: 47
Is it possible to reference properties in an AppleScript?
I’m writing some code for QLab (Sound, video and lighting control for macOS) and I’d like to be able to have a certain property defined/selected/referenced in the first part of the code and then recalled later. I'm looking to reference one kind of property and then be able to reference an other property without having to duplicate my code and changing out that part.
QLab's AppleScript Dictionary can be found here
So my example of something that works without a reference is this:
tell application id "com.figure53.QLab.4" to tell front workspace
set selectedCues to last item of (selected as list) -- selects an item in my software (QLab)
return q name of selectedCues -- This works and returns q name of selectedCues
end tell
I'd like to do the same, but reference the property "q name" and call for it later
In my script I've tried various versions of setting a variable (selected Property) to q name
tell application id "com.figure53.QLab.4" to tell front workspace
set selectedCues to last item of (selected as list) -- selects an item in my software (QLab)
set selectedProperty to q name -- This code doesn't work
return selectedProperty of selectedCues -- This is broken
end tell
EDIT: Added new examples to better explain what I'm looking for:
I've written a script here that with a handler that returns a selected property of selectedCues (items in my software). It lets the user pick from a list and then with if statements grabs the values selected in the dialog:
tell application id "com.figure53.QLab.4" to tell front workspace
set selectedCues to (selected as list)
set parameterChoices to {"q name", "q number", "q type", "q color", "notes"}
choose from list parameterChoices with prompt "Pick a property to return from selected cue" with title "Choose a property" default items {"q Name"}
set selectedProperty to item 1 of result
end tell
returnPropertyOfSelected(selectedProperty, selectedCues)
on returnPropertyOfSelected(cueProperty, cuesToProcess)
tell application id "com.figure53.QLab.4" to tell front workspace
set returnedValue to {}
if cueProperty is "q name" then -- Using if statements here to make returnedValue return selected property
repeat with i in cuesToProcess
set end of returnedValue to (q name of i)
end repeat
else if cueProperty is "q number" then
repeat with i in cuesToProcess
set end of returnedValue to (q number of i)
end repeat
else if cueProperty is "q type" then
repeat with i in cuesToProcess
set end of returnedValue to (q type of i)
end repeat
else if cueProperty is "q color" then
repeat with i in cuesToProcess
set end of returnedValue to (q color of i)
end repeat
else if cueProperty is "notes" then
repeat with i in cuesToProcess
set end of returnedValue to (notes of i)
end repeat
end if
returnedValue
end tell
end returnPropertyOfSelected
Doing it this way I have to make an if statement for every choice the user could make. I'm wondering if there is a way to have this selection referenced and implemented directly into the code so I wont have to make an if statement for every choice possible.
Optimally I'd like something like this:
tell application id "com.figure53.QLab.4" to tell front workspace
set selectedCues to (selected as list)
set parameterChoices to {"q name", "q number", "q type", "q color", "notes"}
choose from list parameterChoices with prompt "Pick a property to return from selected cue" with title "Choose a property" default items {"q Name"}
set selectedProperty to item 1 of result
end tell
returnPropertyOfSelected(selectedProperty, selectedCues)
on returnPropertyOfSelected(cueProperty, cuesToProcess)
tell application id "com.figure53.QLab.4" to tell front workspace
set returnedValue to {}
repeat with i in cuesToProcess
set end of returnedValue to (cueProperty of i) -- This is the part I would like, but it doesn't work
end repeat
returnedValue
end tell
end returnPropertyOfSelected
Is there a way to do this?
Upvotes: 0
Views: 571
Reputation: 724
For sake of completeness only[1], here is how you do property lookups in AppleScript using strings as keys:
to makePropertyAccessor(appName, propertyName)
(*
WARNING: this handler does not sanitize its inputs.
NEVER call from untrusted code. Severe risk of data
destruction from accidental errors/injection attacks.
*)
run script "script
to getProperty(inObject)
tell application \"" & appName & "\"
return " & propertyName & " of inObject
end tell
end getProperty
to setProperty(inObject, newValue)
tell application \"" & appName & "\"
set " & propertyName & " of inObject to newValue
end tell
end setProperty
end script"
end makePropertyAccessor
-- example usage:
set ContainerAccessor to makePropertyAccessor("Finder", "container")
tell application "Finder" to set aRecord to properties of home
ContainerAccessor's getProperty(aRecord)
--> folder "Users" of startup disk of application "Finder"
Like most code generation, this is totally evil and unsafe, and stands excellent chance of doing serious damage should you screw up. While I appreciate that typing out a lengthy if
block is somewhat tedious, such code is simple, clear, reliable, and about as idiot-proof as it gets. And AppleScript is already janky enough without any “clever hacks” on top. KISS.
--
[1] I wouldn’t post it at all except that @red_menace’s implementation is problematic in that 1. it is totally insecure which it fails to mention, and 2. it jumps across different interpreter contexts to do the work so is slow and cannot pass references and other complex values in and out without wrecking them. This does not mean mine is without problems: aside from also being totally insecure, I vaguely recall run script
would leak memory on each call. However, it only has to be called once for each property you want to access, and the reusable object returned executes in the current context so there are no problems with passing values in and out of its handlers.
Upvotes: 1
Reputation: 3422
The main problem with what I think you are trying to do, is that outside of an application tell statement, any given application's scripting terms have no special meaning. One way to use arbitrary scripting terms at run time would be to use the run script
command, since it can use text passed to it as a separate script to run.
Note that this is usually considered a Bad Idea™, especially since trying to use a common handler and passing terms to it most often winds up longer than just using separate commands. You can also usually get a record of all an item's properties, which is also easier than using separate commands. But if you think you need to do stuff like this, you should always verify the terms you plan on using and not allow the user to enter terms (or if you must also do that, make sure you sanitize them).
I don't have the QLab application used in your post, so the following example uses System Events and a couple of different methods to get the property of a file item. One way uses a handler that creates a script using a pre-defined term, and the other gets the value from a record of the item properties :
property itemClass : "class"
property itemType : "type identifier" -- UTI
property itemName : "name"
property itemDefault : "default application" -- alias
set fileItem to (choose file)
log runScript(itemType, fileItem) -- get property by creating a script using a term
tell application "System Events" to set itemProperties to (get properties of disk item (fileItem as text))
log type identifier of itemProperties -- get property from a record of all the properties
to runScript(fileProperty, fileItem) -- get specified file property
return run script "tell application \"System Events\" to return " & fileProperty & " of disk item " & space & quote & fileItem & quote
end runScript
Upvotes: 1