AppleScript and the class struggle
May 9, 2019 at 9:16 AM by Dr. Drang
After a Twitter conversation with ComplexPoint, I have a more elegant solution to the circle counting script I wrote about yesterday.
Both of the problems I had in writing the script are related to AppleScript’s approach to classes and variable typing.
The OmniGraffle dictionary says the fill color
property of a shape is of class color
, not list
, which is presumably why lines like
applescript:
if c is redFill then
didn’t work (recall that variable c
is an item in a list of fill color
s) and I had to do a coercion,
applescript:
if c as list is redFill then
to get the Boolean expression to ever return true
. If AppleScript used duck typing, it would recognize that a color
walks like a list
and talks like a list
and wouldn’t need the coercion.
This morning I wondered if I could get coercion to work the other way. If instead of defining the colors at the top of the script the way I did originally,
applescript:
set redFill to {64614, 0, 1766}
set greenFill to {5246, 39932, 635}
set blueFill to {0, 0, 65416}
I could define them through coercion,
applescript:
set redFill to {64614, 0, 1766} as color
set greenFill to {5246, 39932, 635} as color
set blueFill to {0, 0, 65416} as color
then maybe I could reduce the rest of the script to the short and elegant
applescript:
tell application "OmniGraffle"
tell layer "Annotations" of canvas 1 of document 1
set reds to every shape whose name is "Circle" and fill color is redFill
set greens to every shape whose name is "Circle" and fill color is greenFill
set blues to every shape whose name is "Circle" and fill color is blueFill
end tell
end tell
Unfortunately, this didn’t work. It produced an error on the first line:
Can't make {64614, 0, 1766} into type color.
Update May 9, 2019 9:36 AM
ComplexPoint tells me that
applescript:
set redFill to {64614, 0, 1766} as RGB color
doesn’t produce an error, and he’s right. But the rest of the script doesn’t work. The lists generated by lines like
applescript:
set reds to every shape whose name is "Circle" and fill color is redFill
are empty.
But another approach did lead to an elegant solution. OmniGraffle has a way of assigning data to objects that is completely under the control of the user and there’s no class warfare. If you go to the Info pane headed by the gear icon, you’ll see an Object Data section.
You can type whatever you want into the notes field (the multi-line text area) and be able to access it in AppleScript through the notes
property. So I selected all of my red circles (by clicking on one of them and using ) and entering “red” into the notes field, as above. After doing the same for the green and blue circles, this short script did everything I wanted.
applescript:
1: tell application "OmniGraffle"
2: tell layer "Annotations" of canvas 1 of document 1
3: set reds to every shape whose name is "Circle" and notes is "red"
4: set greens to every shape whose name is "Circle" and notes is "green"
5: set blues to every shape whose name is "Circle" and notes is "blue"
6: end tell
7: end tell
No need for counting variables, no need to loop through lists with repeat
. This is the kind of script I was hoping to write yesterday.
To see how many circles of each type are in the drawing, I look at the inspector pane in Script Debugger:
If I had known this before I started drawing circles, I could have avoided the “select similar objects trick.” Instead, I’d have added the appropriate note to the first circle of each color, and the notes would have propagated every time I duplicated a circle. That’s what I’ll be doing in the future.