I'm getting an unusual result when resizing windows via applescript.
I'm trying to set the position and size of Excel's windows but in one window's case it works and in another it doesn't.
Here's the code:
tell application "System Events"
get the name of every application process whose class of windows contains window
repeat with P in the result
get (every window of process (contents of P) whose value of attribute "AXMinimized" is false)
repeat with W in the result
set win_name to name of W
log P & ":" & win_name
if (win_name is equal to "VBA-Web - Blank") then
log "CASE1"
tell W
set {position, size} to {{0, 0}, {1100, 500}}
end tell
end if
if (win_name is equal to "Microsoft Visual Basic - VBA-Web - Blank.xlsm - [ThisWorkbook (Code)]") then
log "CASE2"
tell W
set {position, size} to {{0, 0}, {1120, 520}}
end tell
end if
end repeat
end repeat
end tell
In the code above, CASE1 works fine, but CASE2 does nothing.
I suspect the VBA editor window is somehow different... but how?
Any suggestions on what I can try to fix this?
Related
I am debugging a legacy desktop automation tool which use AppleScript for UI control on MacOS. One of the script keeps failing with error "System Events got an error: Can’t get static text \"2\" of window \"UISoup – mac_utils.py\" of application process \"pycharm\"." number -1728 from static text "2" of window "UISoup – mac_utils.py" of application process "pycharm", and here is the script
tell application "System Events" to tell process "PyCharm"
set visible to true
set uiElement to static text "2" of window "UISoup – mac_utils.py" of application process "pycharm" of application "System Events"
set layer to 1
if uiElement = null then
set layer to 0
set collectedElements to {UI elements of front window, layer}
else
set layer to layer + 1
set collectedElements to {null, layer}
if name of attributes of uiElement contains "AXChildren" then
set collectedElements to {value of attribute "AXChildren" of uiElement, layer}
end if
end if
end tell
It seems like this line set uiElement to static text "2" of window "UISoup – mac_utils.py" of application process "pycharm" of application "System Events" should set the uiElement to null if the element is not found. But in fact it will throw error instead. How to let it return null instead of throw error to make it works? Thank you.
In the AppleScript equivalent of null is missing value. When GUI scripting, the process should be frontmost. You can check existing of UI elements using command exists (you can use try block as well).
tell application "System Events" to tell process "PyCharm"
set frontmost to true
-- set visible to true
set uiElementExists to exists static text "2" of window "UISoup – mac_utils.py"
set layer to 1
if not uiElementExists then
set layer to 0
set collectedElements to {UI elements of front window, layer}
else
set layer to layer + 1
set collectedElements to {missing value, layer}
set uiElement to static text "2" of window "UISoup – mac_utils.py"
if name of attributes of uiElement contains "AXChildren" then
set collectedElements to {value of attribute "AXChildren" of uiElement, layer}
end if
end if
end tell
Is it possible to get the name of an Illustrator artboard in Applescript?
This script works perfectly until I try to get the name of the artboard:
tell application "Adobe Illustrator"
tell document 1
set artboards_count to count of artboards
set c to 1
repeat while c <= artboards_count
log index of artboard c as text
log artboard rectangle of artboard c as text
log name of artboard c as text -- this line fails
set c to c + 1
end repeat
end tell
end tell
the line log name of artboard c as text fails - everything else works ok.
The message is:
Adobe Illustrator got an error: Can’t get name of artboard 1 of document 1. (-1728)
Any idea as to why?
Setting the name fails too, BTW. However if I do
tell application "Adobe Illustrator"
tell document 1
return properties of artboard 1
end tell
end tell
I get (carriage returns added for clarify):
artboard rectangle:0.0, 0.0, 841.889999999999, -595.280000000001,
ruler PAR:1.0, show center:false, show cross hairs:false,
show safe areas:false, ruler origin:0.0, 0.0, name:Artboard 1,
container:document 1, best type:reference, default type:reference,
class:artboard, index:1
from which one would think the property nameshould be there.
The name property is read-only so there is no way to change it once the artboard exists. And even though you can get the properties of an artboard, you can’t convert it to a string if you simply want to target a particular artboard. But there is a way to do it nonetheless that I discovered by accident. Suppose you have several artboards and want to target the artboard named "Squash this". Here's how to do that:
Tell application "Adobe Illustrator"
tell current document
set artCount to number of artboards
repeat with i from 1 to artCount
set artProp to get properties of artboard i
try
set propString to artProp as string --this will fail
on error
set errorDisp to text of result --this captures the text of the error
set errorDispText to errorDisp as string --changes the text to a ¬
searchable string
end try
if errorDispText contains "quash" then
display notification "errorDispText" --oddly enough, this displays just ¬
the artboard name
exit repeat
end if
end repeat
end tell
end tell
The best solution could be coercion; take the record returned from the Artboard properties and coerce the record into a list. So, your name property from the record becomes the 7th item in the list. The code below will get Artboard dimensions, name and index, and place them into a list for later use in your script.
Hope this helps!
tell application "Adobe Illustrator"
set artboardDetails to {}
tell front document
set allArtboards to every artboard
repeat with i from 1 to count of allArtboards
set thisArtboard to item i of allArtboards
set thisArtboardProps to properties of thisArtboard
set thisArtboardDimensions to artboard rectangle of thisArtboardProps
set thisArtboardIndex to index of thisArtboardProps
--WORKAROUND
set thisArtboardPropsCoerce to thisArtboardProps as list
set thisArtboardName to item 7 of thisArtboardPropsCoerce
set the end of artboardDetails to {thisArtboardName, thisArtboardIndex, thisArtboardDimensions}
end repeat
end tell
end tell
(*
--WORKAROUND
The following is a workaround for error returned from:
set anArtboardName to the name of anArtboard -- this line will not return a result, just errors out
BUT WE NEED TO WATCH OUT FOR THE COERCED LIST!
So, we have to coerce the properties record of artboard to a list first
WEIRD I KNOW!
The coercion then changes the record of 11 items to a list of 12 items
set anArtboardProps to properties of anArtboard as list
set anArtboardName to item 7 of anArtboardProps
NOW LETS MATCH UP THE COERCION ITEMS
The first lines are from the record properties, and the second lines are from the coerced to list versions.
01. artboard rectangle:{0.0, 768.0, 1366.0, 0.0},
01. {0.0, 768.0, 1366.0, 0.0},
02. ruler PAR:1.0,
02. 1.0,
03. show center:false,
03. false,
04. show cross hairs:false,
04. false,
05. show safe areas:false,
05. false,
06. ruler origin:{0.0, 0.0},
06. {0.0, 0.0},
07. name:"Artboard 1",
07. "Artboard 1",
08. container:document 1,
08. document 1,
09. best type:reference,
09. reference,
10. default type:reference,
10. reference,
11. index:1
11. artboard,
12. NULL - nothing in the record
12. 1 - so this line is the index of the artboard
*)
Here is my workaround. As the «class bAl9» vanishes every time you compile, you have to copy/paste every time, but it works. And yes, «class bAl9» will turn to "name" and will not compile correctly the next time. Thanks Adobe !
tell application "Adobe Illustrator"
tell document 1
repeat with x in (every artboard)
log index of x as text
log artboard rectangle of x as text
log «class bAl9» of x as text -- artboard's name property = «class bAl9»
end repeat
end tell
end tell
The name property of artboards is read/write, it's easy to rename artboards this way once you get the trick.
Edit : For the loop in a list, I always use the repeat with x in l statement. It's smart and fast.
Stu's answer was the best that I found. Coercing the record to a list is brilliant – I didn't even know you could do that. My code was:
repeat with i from 1 to the (count of AllArtBoards)
set ThisArtBoard to item i of AllArtBoards
set ThisArtBoardName to item 7 of ((properties of ThisArtBoard) as list)
set the end of AllArtBoardNames to ThisArtBoardName
end repeat
I have been using this script in Automator, which toggles apps between full-screen and windowed mode. I am a frequent user of split-screen applications (introduced in El Capitan), so is there any way to modify this script to enable split-screen? I know there's no keyboard shortcut for splitting, so this is definitely a shot in the dark.
I managed to cobble something together, working off an AppleScript/python thing that I found in this MacScripter post. It does the following:
Pulls a list of open application windows from System Events, and allows the user to select one or two (for full screen or split screen)
Launches "Mission Control"
GUI-scripts the Dock to find references to the various window-buttons and spaces in Mission Control that we need to access
Programmatically drags the buttons around to create a new fullscreen or splitscreen space
Clicks on the newly created space to activate it
You might be able to trim some time down on all the half-second delays, but Mission Control is expecting human interaction and handles things lazily. It will miss GUI requests that come too fast.
(* collect names of open app windows *)
tell application "System Events"
set windowNames to {}
set theVisibleProcesses to every process whose visible is true
repeat with thisProcess in theVisibleProcesses
set windowNames to windowNames & (name of every window of thisProcess whose role description is "standard window")
end repeat
end tell
(* choose 1 name for fullscreen, two names for split screen *)
set selectedItems to choose from list windowNames with title "Split It" with prompt "Choose items to add to split view." with multiple selections allowed without empty selection allowed
if selectedItems is false or (count of selectedItems) > 2 then return
set selectedWindow1 to item 1 of selectedItems
if (count of selectedItems) = 2 then
set selectedWindow2 to item 2 of selectedItems
end if
tell application "Mission Control"
launch
end tell
(*
The dock has a set of nested UI elements for Mission Control, with the following structure:
"Mission Control"'s group 1 (the base container)
group 1, group 2, .... (groups for each desktop)
buttons for open windows on each desktop
group "Spaces Bar"
a single button (the '+' buttan to add a new space)
a list
buttons for the desktops and any already-made spaces
*)
tell application "System Events"
tell process "Dock"'s group "Mission Control"'s group 1
tell group "Spaces Bar"'s list 1
set {p, s} to {position, size} of last UI element
set XTarget to (item 1 of p) + (item 1 of s) + 100
set YTarget to (item 2 of p) + (item 2 of s) + 100
end tell
tell group 1
set viewButton1 to button selectedWindow1
set {x, y} to get viewButton1's position
my mouseDrag(x, y, XTarget, YTarget, 0.5)
end tell
tell group "Spaces Bar"'s list 1
set {p, s} to {position, size} of last UI element
set XTarget to (item 1 of p) + (item 1 of s) + 10
set YTarget to (item 2 of p) + (item 2 of s) + 10
end tell
try
tell group 1
set viewButton2 to button selectedWindow2
set {x, y} to get viewButton2's position
my mouseDrag(x, y, XTarget, YTarget, 0.5)
end tell
end try
tell group "Spaces Bar"'s list 1
delay 0.5
set lastUI to last UI element
click lastUI
end tell
end tell
end tell
on mouseDrag(xDown, yDown, xUp, yUp, delayTime)
do shell script "
/usr/bin/python <<END
from Quartz.CoreGraphics import CGEventCreateMouseEvent
from Quartz.CoreGraphics import CGEventCreate
from Quartz.CoreGraphics import CGEventPost
from Quartz.CoreGraphics import kCGEventLeftMouseDown
from Quartz.CoreGraphics import kCGEventLeftMouseUp
from Quartz.CoreGraphics import kCGMouseButtonLeft
from Quartz.CoreGraphics import kCGHIDEventTap
from Quartz.CoreGraphics import kCGEventLeftMouseDragged
from Quartz.CoreGraphics import kCGEventMouseMoved
import time
def mouseEvent(type, posx, posy):
theEvent = CGEventCreateMouseEvent(None, type, (posx,posy), kCGMouseButtonLeft)
CGEventPost(kCGHIDEventTap, theEvent)
def mousemove(posx,posy):
mouseEvent(kCGEventMouseMoved, posx,posy);
def mousedrag(posx,posy):
mouseEvent(kCGEventLeftMouseDragged, posx, posy);
def mousedown(posxdown,posydown):
mouseEvent(kCGEventLeftMouseDown, posxdown,posydown);
def mouseup(posxup,posyup):
mouseEvent(kCGEventLeftMouseUp, posxup,posyup);
ourEvent = CGEventCreate(None);
mousemove(" & xDown & "," & yDown & ");
mousedown(" & xDown & "," & yDown & ");
time.sleep(" & (delayTime as text) & ");
mousedrag(" & xUp & "," & yUp & ");
time.sleep(" & (delayTime as text) & ");
mouseup(" & xUp & "," & yUp & ");
END"
end mouseDrag
I agree with Ted Wrigley's answer above, but I would make the following changes:
(...)
tell application "System Events"
tell process "Dock"'s group "Mission Control"'s group 1
tell group "Spaces Bar"'s list 1
set countSpaces to count of UI elements
set {p, s} to {position, size} of last UI element
set XTarget to (item 1 of p) + (item 1 of s) + (100 * countSpaces)
set YTarget to (item 2 of p) + (item 2 of s) + 100
end tell
(...)
If you want to run this script multiple times, you need to count the number of open Spaces to correctly find the right spot to drag the window to. Otherwise, the windows are always are always dragged to the 2nd position of the Spaces Bar.
I am trying to find a way to bring up the context menu in Finder on a Mac with Yosemite without touching the mouse/touchpad.
A context menu.
After extensive research on this issue, the only possible route seems to be using AppleScript with Automator, and assign keyboard shortcut to it.
The AppleScript below was found on stackoverflow, if I run it inside the Automator, it would bring up the context menu on one of the files on the desktop (not the file currently selected.)
tell application "System Events"
tell process "Finder"
set target_index to 1
set target to image target_index of group 1 of scroll area 1
tell target to perform action "AXShowMenu"
end tell
end tell
Automator screenshot
But I am having trouble getting it to work with keyboard shortcut.
Also I will need to make sure that it brings the menu for the currently selected file.
Can someone provide some insight about how this can be done?
You can read about the script below here: MacScripter / right click
# Copyright © 2012 - 2015 McUsr
run showRightClickMenu
script showRightClickMenu
on run
set mouseLoc to (do shell script "~/opt/bin/MouseTools -location")
set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}
tell mouseLoc to set {mouseX, mouseY} to {it's text item 1, it's text item 2}
set {mouseX, mouseY} to {(mouseX as integer), 1200 - (mouseY as integer)}
tell application id "sevs"
set frontProcessName to name of every process whose frontmost is true
-- tell a to set aa to (get its name)
set wnCount to count of windows of process named frontProcessName
if wnCount > 0 then
tell window 1 of process named frontProcessName
set wnPos to its position
set wnsize to its size
end tell
set {wnX, wnY, wnWidth, wnHeight} to {item 1 of wnPos, item 2 of wnPos, item 1 of wnsize, item 2 of wnsize}
set {leftBound, RightBound, upperBound, lowerBound} to {wnX + 1, (wnX + wnWidth - 21), wnY + 50, (wnY + wnHeight - 51)}
if mouseX ≥ leftBound and mouseX ≤ RightBound then
else if mouseX < leftBound then
set mouseX to leftBound
else
set mouseX to RightBound
end if
if mouseY ≥ upperBound and mouseY ≤ lowerBound then
else if mouseY < upperBound then
set mouseY to upperBound
else
set mouseY to lowerBound
end if
end if
end tell
set mouseLoc to "c" & mouseX & " " & mouseY
do shell script "~/opt/bin/cliclick " & mouseLoc
set AppleScript's text item delimiters to astid
end run
end script
This will bring up the context menu of the currently selected file of the desktop:
tell application "Finder"
set sel to get the selection
if (sel is {}) then
log "Nothing selected! Can't proceed"
return
end if
set target_item_name to the name of (item 1 of sel)
end tell
tell application "System Events"
tell process "Finder"
tell group 1 of scroll area 1
set target to the first image whose value of attribute "AXFilename" is target_item_name
tell target to perform action "AXShowMenu"
end tell
end tell
end tell
*Tested on 10.8.5 in script editor
I'm trying a access the complete reference for a cell in Applescript. So far I've managed to get the cell reference and the table reference using a script like:
tell application "Numbers"
tell document 1
repeat with i from 1 to count of sheets
tell sheet i
repeat with j from 1 to count of tables
tell table j
try
set currentCell to the first cell of the selection range
return name of currentCell
end try
end tell
end repeat
end tell
end repeat
end tell
end tell
I can't seem to get the same structure to work for getting the sheet or the document reference. I have tried accessing the properties of the cell and I get something like:
{column:column "A" of table "Table 1" of sheet "Sheet 1" of document "Untitled" of
application "Numbers", alignment:center, value:0.0, background color:{59111, 59111,
59111}, text color:{0, 0, 0}, font size:10.0, vertical alignment:top, name:"A1",
class:cell, font name:"HelveticaNeue", format:automatic, row:row "1" of table "Table
1" of sheet "Sheet 1" of document "Untitled" of application "Numbers", text
wrap:true}
The column property of a cell therefore seems to include the complete reference but if I access the reference directly through the column property.
Can anyone give me some guidance as to how I get the sheet and document using applescript.
Cheers
Ian
Found the solution, fairly simple as long as the code is in the right order:
tell application "Numbers"
tell document 1
repeat with i from 1 to count of sheets
tell sheet i
repeat with j from 1 to count of tables
try
tell table j
set currentCell to the first cell of the selection range
set therow to row of currentCell
set sheetNumber to i
end tell
end try
end repeat
end tell
end repeat
return name of sheet sheetNumber
end tell
end tell
Can use similar code to check for the document number