Check whether scroll is finished using AppleScript - scroll

I have an AppleScript script to open the Mail app, open a mail, scroll down and take screenshot of the whole mail body. I am taking the screenshot by scrolling the page using keystroke 121 (page down). I don't know how much time I have to repeat the loop to reach the page end. Is there anyway we can identify if the scroll has reached the page end using AppleScript?
This is my code snippet for page down and taking screenshot:
open each_message
tell application "System Events"
delay 5
keystroke "f" using {command down, control down}
delay 3
key code 20 using {command down, shift down}
delay 5
key code 121
delay 5
key code 20 using {command down, shift down}
delay 5
keystroke "f" using {command down, control down}
end tell

After reading your question and ensuing comments...
Is there anyway we can identify if the scroll has reached the page end using AppleScript?
Yes
For some emails, it's fine. For some, it is duplicating the last page, for some, the screenshots are incomplete.   (From comments under the question.)
That is the nature of different length documents and pressing the Page Down key, and why maybe using a third-party application like Snagit is a better way to go.
That all said...
The example AppleScript code, shown below, was tested in Script Editor under macOS Catalina with Language & Region settings in System Preferences set to English (US) — Primary and worked for me without issue1.
1 Assumes necessary and appropriate settings in System Preferences > Security & Privacy > Privacy have been set/addressed as needed.
As you did not include the complete code of your script, the following example AppleScript code is presented more as a proof of concept. It too may/will have extra screenshots, however, it ensures the page it all the way to the top before it starts taking the screenshots and ensures a screenshot of the whole last page is taken too, based on it stopping appropriately.
For testing purposes I started with an individual email opened in its own window, assuming that is what the open each_message at the top of the code you included does or represents.
Example AppleScript code:
tell application "Mail" to activate
delay 1
tell application "System Events"
keystroke "f" using {command down, control down}
delay 1
repeat while my getEmailFullScreenWindowScrollPosition() > 0.0
key code 116
delay 0.1
end repeat
delay 1
repeat while my getEmailFullScreenWindowScrollPosition() < 1.0
key code 20 using {command down, shift down}
delay 1
key code 121
delay 1
end repeat
key code 121
delay 1
key code 20 using {command down, shift down}
delay 1
keystroke "f" using {command down, control down}
end tell
to getEmailFullScreenWindowScrollPosition()
tell application "System Events" to ¬
return value of ¬
scroll bar 1 of ¬
scroll area 1 of ¬
window 1 of ¬
process "Mail"
end getEmailFullScreenWindowScrollPosition
Notes:
In Mail in macOS Catalina on my system there was no horizontal scroll bar when an opened email in its own window is in Full Screen view, and why the handler is directed to scroll bar 1. However, if you do need to query for its orientation, then for the handler use:
to getEmailFullScreenWindowScrollPosition()
tell application "System Events" to ¬
return value of ¬
first scroll bar of ¬
scroll area 1 of ¬
window 1 of ¬
process "Mail" whose ¬
orientation is ¬
"AXVerticalOrientation"
end getEmailFullScreenWindowScrollPosition
In testing the getEmailFullScreenWindowScrollPosition() handler, it would report 1.0 when there was still some page left but not showing on the screen, so I added the additional lines of code after the second repeat loop to ensure it was all captured.
The test email had content to the very end of the page and maybe isn't really representative of most emails. If you find that there is ample white space at the end of your emails then you may want to remove the following lines of code after the second repeat loop:
key code 121
delay 1
key code 20 using {command down, shift down}
Also note that I ended up using delay 1 for all but one of the delay commands, and it worked fine for me in testing, however, adjust as necessary for your system.
As this is just being presented as a proof of concept, I'll leave it to you to tweak the code to fit your needs.
Note that I am not affiliated with the developer of Snagit and have mentioned it as a possible option to capturing the entire scroll area of the screen in a single screenshot.
Note: The example AppleScript code is just that and sans any included error handling does not contain any additional error handling as may be appropriate. The onus is upon the user to add any error handling as may be appropriate, needed or wanted. Have a look at the try statement and error statement in the AppleScript Language Guide. See also, Working with Errors. Additionally, the use of the delay command may be necessary between events where appropriate, e.g. delay 0.5, with the value of the delay set appropriately.

Here's a handler that returns the current vertical scroll bar position for a message viewed in the frontmost message viewer. It makes a bunch of assumptions about which panels you have open and what orientation the viewer has, but it should give you something to work with:
set sp to my findMailScrollPosition()
on findMailScrollPosition()
tell application "System Events"
tell process "Mail"
tell window 1
tell splitter group 1
tell splitter group 1
tell scroll area 2
tell (first scroll bar whose orientation is "AXVerticalOrientation")
-- this will return a value between 0 and 1 (top to bottom)
return value
end tell
end tell
end tell
end tell
end tell
end tell
end tell
end findMailScrollPosition

Related

How to "Wait until web page loaded" in Firefox (applescript)?

This code from here works perfect, but doesn't work in Firefox. Why and how to fix it in applescript?
tell application "Google Chrome"
repeat until (loading of tab 1 of window 1 is false)
1 + 1 --just an arbitary line
end repeat
loading of tab 1 of window 1 --this just returns final status
end tell
As Firefox does not contain a specific AppleScript dictionary, e.g. Firefox.sdef file, it is not considered to be AppleScript scriptable in the same way as e.g. Google Chrome, and while it will respond to some of the basic commands, such as activate, quit, etc., it will require UI Scripting to do what you are asking.
The following example AppleScript code requires Firefox version 87 or newer, and setting its accessibility.force_disabled preference to: -1
First, in Firefox, in the Address/Search combo box, type about:config and press enter.
If applicable, click the Accept the Risk and Continue button.
In the Search preference name text box, type accessibility.force_disabled and press enter.
Click the Edit button and change its value to: -1
Click the the Save button.
Here is an example of how I would instruct Firefox to open a new window to a given URL and wait for the page to finish loading.
Example AppleScript code:
set theURL to "https://news.google.com/"
tell application "Firefox" to activate
delay 0.5 -- # Value may need to be adjusted if Firefox is closed.
my clickApplicationMenuCommand("Firefox", "File", "New Window")
delay 0.5
my setURLofFirefoxFrontWindowTo(theURL)
my waitForFirefoxPageToFinishLoading()
say "foobar"
-- # Handler(s) #
to clickApplicationMenuCommand(appName, appMenuName, appMenuCommand)
tell application appName to activate
delay 0.25
tell application "System Events" to ¬
click ¬
menu item appMenuCommand of ¬
menu appMenuName of ¬
menu bar item appMenuName of ¬
menu bar 1 of ¬
application process appName
end clickApplicationMenuCommand
to setURLofFirefoxFrontWindowTo(theURL)
tell application "System Events"
tell application process "Firefox"
set the value of UI element 1 of ¬
combo box 1 of toolbar "Navigation" of ¬
first group of front window to theURL
key code 36 -- # enter key
end tell
end tell
end setURLofFirefoxFrontWindowTo
to waitForFirefoxPageToFinishLoading()
-- # Requires Firefox version 87 or newer.
-- # Requires accessibility.force_disabled set to: -1
tell application "System Events"
tell application process "Firefox"
repeat until exists UI element "Reload" of ¬
toolbar "Navigation" of group 1 of window 1
delay 0.1
end repeat
repeat while (name of UI elements of ¬
toolbar "Navigation" of group 1 of ¬
window 1 whose description is "Reload") ¬
is not {"Reload"}
delay 0.1
end repeat
end tell
end tell
end waitForFirefoxPageToFinishLoading
Notes:
The example AppleScript code, shown above, was tested in Script Editor under macOS Catalina with Language & Region settings in System Preferences set to English (US) — Primary and worked for me without issue1.
1 Assumes necessary and appropriate settings in System Preferences > Security & Privacy > Privacy have been set/addressed as needed.
Tested using Firefox version 91.0 (64-bit).
Note that UI Scripting is very kludgy and is prone to failure, especially as the version of the OS and or application change, or the value of the delay commands are not sufficient. That said however, with Firefox, this as described herein is what it takes.
Note: The example AppleScript code is just that and sans any included error handling does not contain any additional error handling as may be appropriate. The onus is upon the user to add any error handling as may be appropriate, needed or wanted. Have a look at the try statement and error statement in the AppleScript Language Guide. See also, Working with Errors. Additionally, the use of the delay command may be necessary between events where appropriate, e.g. delay 0.5, with the value of the delay set appropriately.

Get text from current cursor position From Pages app in Mac

Is it possible to get the text of Pages app, from its current cursor position?
My requirement is like, when user type something in "Pages", I have to show suggestions for the word they are typing.
so I want to find out the current or last word, near current cursor position from "Pages" app.
Either by using AppleScript or Accessibility?
Text is not selected.
I am not looking for "Services" also.
For apps other than "Pages", I used Accessibility and appleScript. but for pages I am not finding any way.
I have also tried below AppleScript, but some reason it works perfectly in "Script Editor", but when I use it in my code, it goes to infinite loop.
tell application "Pages"
activate
end tell
tell application "System Events"
tell application "System Events"
key code 123 using {shift down, command down} -- shift-command-left
end tell
tell process "Pages"
keystroke "c" using {command down}
delay 1
tell application "System Events"
key code 124 -- shift-command-left
end tell
set myData to (the clipboard) as text
return myData
end tell
end tell
If I run this AppleScript in my app, it freeze my Mac only, I have to force quit the Mac to stop it.
This works for me using the latest versions of macOS Mojave and Pages
property theApp : "Pages" -- change value to name of any other application (TextEdit)
tell application theApp to activate
delay 3
tell application "System Events"
tell application process theApp
-- Move the insertion point to the beginning of the previous word.
key code 123 using {option down} -- left arrow key while holding option down
delay 0.2
-- Move the insertion point to the end of the next word. (selects the word)
key code 124 using {shift down, option down} -- right arrow key while holding option and shift down
delay 0.2
keystroke "c" using {command down} -- copies selected wprd
delay 0.2
-- Next 2 key code commands attempt to restore cursor location
key code 124 using {option down} -- right arrow key while holding option down
delay 0.2
key code 123 using {option down} -- left arrow key while holding option down
tell current application to set myData to (the clipboard) as text
delay 4
return myData
end tell
end tell

AppleScript keystroke

I use an app, and I want to get a data from this app,
I found a button on the menu bar > Edit > Optionwanted
I also found that the option have a shortcut (C + COMD + CTRL + SHIFT)
I'm not sure this is the best approach but I tried :
activate application "App"
delay 1
tell application "System Events" to keystroke "C" using {control down, command down, shift}
but then the result is
System Events got an error: Can’t make {control down, command down,
shift} into type constant or list of constant.
is that the best approach to get text from an App ?
I can I fix my script.
PS : when I run
tell application "System Events" to tell application process "App"
set stuff to entire contents of front window
end tell
return stuff
I can see what I want there :
application process "App", static text "445511" of group 3 of
group 1 of group 2 of group 5 of UI element 1 of scroll area 1 of
group 2 of group 1 of group 2 of UI element 1 of scroll area 1 of
splitter group 1 of window "The Title is actually not static"
That can be a another approach but the title and Text is dynamic and change all the time.
You are close.
This is what I got:
activate application "App"
delay 1
tell application "System Events"
keystroke "C" using {control down, command down, shift down}
end tell

applescript copy paste between textedit and 3rd party application not working

I have searched all over this site and cannot find an answer to why this code isn't doing what I need it to do. I have a textedit doc with a list of numbers. I want to copy 1 number at a time paste that number into a 3rd party application at a specific place in url, and then hit some buttons in the ui of that application. I need this process to repeat for each individual number in the textedit document.
Here is what I came up with after researching applescript.
tell application "TextEdit" to activate
tell application "System Events"
tell process "TextEdit"
key code 124 using {shift down, command down}
keystroke "c" using command down
key code 125
end tell
end tell
delay 1.0
tell application "import.io" to activate
tell application "System Events"
tell process "import.io"
keystroke tab
keystroke tab
key code 124
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 123
key code 51
keystroke "v" using command down
keystroke tab
key code 76
end tell
end tell
-- Make a selection from the popupbutton.
delay 2.231426
set timeoutSeconds to 10.0
set uiScript to "click pop up button 1 of window \"Save\" of application process \"import.io\""
my doWithTimeout(uiScript, timeoutSeconds)
return input
end run
on doWithTimeout(uiScript, timeoutSeconds)
set endDate to (current date) + timeoutSeconds
repeat
try
run script "tell application \"System Events\"
" & uiScript & "
end tell"
exit repeat
on error errorMessage
if ((current date) > endDate) then
error "Can not " & uiScript
end if
end try
end repeat
end doWithTimeout
-- Click the “<fill in title>” checkbox.
delay 1.496275
set timeoutSeconds to 10.0
set uiScript to "click checkbox 1 of window \"Save\" of application process \"import.io\""
my doWithTimeout(uiScript, timeoutSeconds)
return input
-- Type “Data” into the text field.
delay 7.290406
set timeoutSeconds to 10.0
set uiScript to "click text field 1 of group 17 of list 1 of scroll area 1 of scroll area 1 of browser 1 of splitter group 1 of splitter group 1 of group 2 of window \"Save\" of application process \"import.io\""
keystroke "Data"
keystroke "."
tell application "System Events" to tell process "import.io"
keystroke "v" using command down
end tell
my doWithTimeout(uiScript, timeoutSeconds)
return input
-- Click the “Save” button.
delay 1.475013
set timeoutSeconds to 10.0
set uiScript to "click UI Element \"Save\" of window \"Save\" of application process \"import.io\""
my doWithTimeout(uiScript, timeoutSeconds)
return input
my textedit document is formatted like this:
50
100
150
200
etc
When I run the script this is what it does to my textedit document:
50
50
100
150
200
etc
Any idea what is going on here? I can't make heads or tails of it.
Looking for result in your TextEdit file, first you must start to simplify the textEdit copy part. I assumed that the TextEdit document is already open.
The script bellow provides simplification for copy part :
tell application "TextEdit"
activate
set myNumbers to every paragraph of front document
end tell
repeat with aNumber in myNumbers -- loop through each number
set the clipboard to aNumber
-- insert here the paste instructions in your third party application
end repeat
You must insert the instruction about the paste in the loop, after the 'set clipboard...' instruction which fills the clipboard with a number.
I can't help you for the paste part, because I don't know your third party application, however, your program calling other scripts in doWithTimeout routine does not seems to be very clean and efficient.
If this third party application is not scriptable, at least you may have to go via GUI scripting or java (if java based application).
For instance, instead of doing all the key arrows to move to correct input cell, try to address that cell directly by its GUI properties.
Same for the checkbox, you can also use GUI click function.
The disadvantage of GUI scripting is that application interface must not changed. But this is already the case in your script.

Using AXIdentifier in UI Scripting for Applescript

10.7.4 OSX Lion
Applescript
I am working with an application (built in house and has no Applescript dictionary) that has a static text element I want to copy to the clipboard and send to another app but I'm having a hard time getting it to work.
The script I was using for targeting the element looked like this:
Tell application "System Events" to set frontmost of process "*application*" to true
Tell application "System Events"
Tell process "*application*"
Tell static text 1 of tab view 1 scroll area 1 of splitter group 1 of splitter group 1 of splitter group 1 of window 1
keystroke "a" using command down
delay 0.1
keystroke "c" using command down
delay 0.1
end tell
end tell
end tell
end tell
What would happen was that the wrong text from the wrong element was copied to the clipboard every time I clicked in a different spot on the application (there are numerous text fields).
I noticed in UI Accessor/Accessibility Accessor that each UI element in the application has a unique AXIdentifier value when you mouse over them.
Is there anyway to accomplishing what I am trying to do, using AXIdentifier values to target that element and copy the text from it?
Thanks for all the help this is my first post and I hope it was worthy! ~TheLarkInn
You can do this by using AppleScript's filtering. For example, to get the From: pop-up menu in a message composition window in Apple Mail, there is no accessibility description you can match on, however there is a unique AXIdentifier which you can match as follows:
tell application "System Events"
tell application process "Mail"
tell window 1
get first pop up button whose value of attribute "AXIdentifier" is "popup_from"
end tell
end tell
end tell
This is more efficient than looping in AppleScript as it only involves sending one Apple Event to System Events.
I don't think there is a way to directly do what you're trying to do. It seems like you can only access attributes once you have a handle on an element via selector. Here is a very ugly solution that does what you're asking by iterating over all UI elements, but it is really slow with bigger UIs and probably not ideal for any production level code.
tell application "System Events"
tell process "Some Process"
set tElements to entire contents of window "Some Window"
repeat with tElement in tElements
if (exists attribute "AXIdentifier" of tElement) then
if value of attribute "AXIdentifier" of tElement = "Some AXIdentifier" then set tText to value of tElement
end if
end repeat
end tell
end tell
tText
I think using UIElementInspector or Accessibility Inspector from Xcode to build a selector string is the way to go!
Tell application "*application*" to activate
Tell application "System Events"
Tell application process "*application*"
set textStaticTextValue to value of static text 1 of tab view 1 scroll area 1 of splitter group 1 of splitter group 1 of splitter group 1 of window 1
end tell
end tell

Resources