AppleScript: cannot get property of window - applescript

I'm a newbie in AppleScript.
I'm trying to write a short automator script that involves getting/setting window's miniaturized (or minimized for some apps apparently).
I'm totally lost at the point: Why the first attempt works but not the second one in the following codes?
# Run this, and switch to Google Chrome, within 3 seconds
delay 3
# This works
tell application "/Applications/Google Chrome.app/"
log (get minimized of first window)
end tell
tell application "System Events"
set process_bid to get the bundle identifier of (first application process whose frontmost is true)
set application_name to file of (application processes where bundle identifier is process_bid)
end tell
set front_app to POSIX path of (application_name as string)
# They're same
log (front_app = "/Applications/Google Chrome.app/")
# Then why is this not working?
tell application front_app
log (get minimized of first window)
end tell

The argument of tell application must be a literal (a constant) because the terminology of the application inside the tell block is evaluated at compile time.
You could add a using terms from block, then the argument of tell application can be a variable. But this requires the argument of the block to be a constant.

# Run this, and switch to Google Chrome, within 3 seconds
delay 3
# This works
tell application id "com.google.Chrome" to log (get minimized of first window)
tell application "System Events"
set process_bid to bundle identifier of (1st process whose frontmost is true)
end tell
# They're same
log (process_bid = "com.google.Chrome")
log (run script ("tell application id \"" & process_bid & "\" to minimized of window 1"))
Another (simple) example for testing:
set process_bid to "com.google.Chrome"
run script ("tell application id \"" & process_bid & "\" to minimized of window 1")

Related

get next-to-last window index with applescript

I'm having trouble reconciling these cases. 3 and 4 are exactly the same except for the application. Same for 5 and 6. I also substituted safari with brave. They seem to work the same. textedit also seems to work like the others. Is finder just a special case or is there something else going on?
# Case 1) Give tell a string, get last window index: succeeds.
tell application "finder"
get index of last window
end tell
# Case 2) Give tell a variable, get last window index: succeeds.
set the_application to "finder"
function_a(the_application)
on function_a(the_application)
tell application the_application
get index of last window
end tell
end function_a
# Case 3) Give tell a variable, get next-to-last window index for finder: fails.
# 651:656: execution error: Finder got an error: Can’t get window before Finder window id 63457. (-1728)
set the_application to "finder"
function_b(the_application)
on function_b(the_application)
tell application the_application
get index of window before last window
end tell
end function_b
# Case 4) Give tell a variable, get next-to-last window index for safari: succeeds.
set the_application to "safari"
function_c(the_application)
on function_c(the_application)
tell application the_application
get index of window before last window
end tell
end function_c
# Case 5) Give tell a string, get next-to-last window index: succeeds.
tell application "safari"
get index of window before last window
end tell
# Case 6) Give tell a string, get next-to-last window index: fails.
1420:1425: execution error: Finder got an error: Can’t get window before Finder window id 63457. (-1728)
tell application "finder"
get index of window before last window
end tell
# Case 7) Give tell a string, get next-to-last finder window index: succeeds.
tell application "finder"
get index of finder window before last window
end tell
Any scriptable application has an unique AppleScript dictionary.
Of course applications can have something in common like windows and documents but the implementation of the windows element and the treatment of indices are individual. There is no general convention how to do that. Even properties with the same name can have different four-character-codes (the internal terminology identifier) in different applications.
For example the NSPositionalSpecifier must be implemented explicitly in the target application to be able to use the before/after syntax.
In my option the effort to write reusable scripts for multiple applications is a waste of time.
I will show you the solution for the Safari as example.
Case ONE (probably, it is your case): you Safari's settings is "open new windows as windows":
set the_application to "Safari"
function_c(the_application)
on function_c(the_application)
run script "tell application \"" & the_application & "\"
get index of window before last window
end tell"
end function_c
Case TWO (if you Safari's settings is "open new windows as tabs"):
set the_application to "Safari"
function_c(the_application)
on function_c(the_application)
run script "tell application \"" & the_application & "\"
get index of tab before last tab of window 1
end tell"
end function_c
The same as the code above, but for the multiple applications:
set the_application to choose from list {"Safari", "Finder", "TextEdit"}
if the_application is false then return
set the_application to item 1 of the_application
function_c(the_application)
on function_c(the_application)
run script "tell application \"" & the_application & "\"
get index of last window
end tell"
end function_c
Other beautiful example:
return {|Safari|:function_c("Safari"), |Finder|:function_c("Finder"), |TextEdit|:function_c("TextEdit")}
on function_c(the_application)
try
run script "tell application \"" & the_application & "\"
get index of last window
end tell"
on error
return missing value
end try
end function_c

using applescript in textwrangler to send code to julia

I am trying to setup a TextWrangler script that automatically sends selected code to Julia. Copying a bit from a similar script that does the job for sending code to R I tried the script
tell application "TextWrangler"
set the_selection to (selection of front window as string)
if (the_selection) is "" then
set the_selection to line (get startLine of selection) of front window as string
end if
end tell
tell application "Julia-0.2.1"
activate
cmd the_selection
end tell
It does not work. Probably because of the line containing "cmd the_selection". Anyone a suggestion on how to fix this?
Well if Julia isn't scriptable as #khagler says, then you can usually use gui scripting. This will work as long as Julia has an edit menu and the keyboard shortcut for paste is cmd-v (like in most applications) and your cursor is placed in the proper location for the paste to work. Good luck.
tell application "TextWrangler"
set the_selection to (selection of front window as string)
if (the_selection) is "" then
set the_selection to line (get startLine of selection) of front window as string
end if
end tell
set the clipboard to the_selection
tell application "Julia-0.2.1" to activate
delay 0.2
tell application "System Events" to keystroke "v" using command down
EDIT: I see in your comments that a new Julia window opens every time the script is run. That might be happening because of the line... tell application "Julia" to activate. You might try this line of code in its place. It's another method to bring Julia frontmost before issuing the keystroke commands.
tell application "System Events" to tell process "Julia-0.2.1" to set frontmost to true
Note that if this doesn't bring the Julia window frontmost then it's because the process name is wrong. Sometimes the process name is different than the application name. If true you'll have to figure out the name of the Julia process which you can do by running this and finding its name. You may have to use "Terminal" if Julia runs in a Terminal window.
tell application "System Events" to return name of processes
Julia is not scriptable, so you won't be able to send your code this way. You could try this:
do shell script "julia -e '" & the_selection & "'"
This is equivalent to typing a command into a Terminal window. Depending on what exactly is in your selection, you might need to save it as a file first and then pass the file path instead. Also, note that if julia is not in your path (as would be the case if you just copied the Julia app to your Applications folder) you'll need to specify the full path to the actual executable within the .app package: /Applications/Julia-0.2.1.app/Contents/Resources/julia/bin/julia.

AppleScript: Get list of windows on all desktops

I need to get a count of all windows per application. Every way I've tried to do this, I only get a count of windows that are assigned to the current (Mission Control) Desktop. (I'm currently running Mac OS X 10.7, so post-Spaces.) Is there any way to get a per-application count of all windows across all Desktops?
The crux of what I've tried:
tell application "System Events"
repeat with _app in (every process whose visible is true)
tell _app
log (name as string) & ": " & (count of every window)
end tell
end repeat
end tell
Note that the whose visible is true clause isn't the problem. It finds all of the appropriate processes, but once I ask the processes for windows, they only count the ones in the active Desktop.
I've tried pulling the log line out of the tell and using name of _app and count of every window of _app, but there's no difference. I've tried grabbing things other than processes from System Events, but anything useful ends up effectively being just a different way to get the same object. I've tried iterating over UI elements, but no windows show up that aren't on the current Desktop, though I do get a menubar for each application.
I'm fine with iterating across all Desktops (though not actually switching to all of them), but I can't even find a way to get a list of Desktops. This answer claims to describe how to do that, but I only ever get a single element inside every desktop. Not that there's an obvious way to get windows once you have that Desktop object anyway.
It's also worth pointing out that desktops are controlled by the Dock, and not by Mission Control. I'm not aware of any way for AppleScript to talk to the Dock, so if you know of something, then an answer or comment about that might help point me in the right direction.
Am I trying to do something impossible?
I ran your code, setting up the Applescript version to 2.4 (do that as a habit); on the first run, your code presented me with the appropriate count of all the windows per application.
This code is what I tried, and the results seem to be satisfactory. Is there something I'm not seeing?
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
tell application "System Events"
set my_list to {}
repeat with _app in (every process whose visible is true)
tell _app
log (name as string) & ": " & (count of every window)
set my_list to my_list & {name as string, count of every window}
end tell
end repeat
log my_list
end tell
As the OP is over six years old and I am unable to test under OS X 10.7, which is what was being used at that time, nonetheless, the following example AppleScript code works for me under macOS Catalina and returned the correct window count across all Desktops/Spaces, with the exception of any application that does understand the given AppleScript command and why the try and on error statements are being used.
Example AppleScript code:
tell application "System Events" to ¬
set appBundleIdentifierList to ¬
the bundle identifier of ¬
(every process whose visible is true)
repeat with appBundleIdentifier in appBundleIdentifierList
try
tell application id appBundleIdentifier to ¬
set {appName, winCount} to {name, (count windows)}
log appName & ": " & winCount
on error errorMessage
log errorMessage
end try
end repeat
Sample output on my system that has multiple Desktops/Spaces with windows of the application on all or some of the Desktops/Spaces and the window count for each is correct across all Desktops/Spaces, not just the active Desktop/Space the script was run from.
(*Safari: 6*)
(*Terminal: 2*)
(*TextEdit: 4*)
(*Script Editor: 7*)
(*Finder: 3*)
(*BBEdit: 1*)
(*Norton Secure VPN got an error: every window doesn’t understand the “count” message.*)
(*Music: 2*)
Notes:
Not all applications are AppleScript scriptable, in that some do not contain an AppleScript dictionary within their application bundle.
Since the application process cannot return the correct number of windows across all Desktops/Spaces, this method relies on the application to return the number of windows across all Desktops/Spaces.
Update:
The following example AppleScript code does the following:
Gets the bundle identifier of every process whose visible is true.
For each bundle identifier, get its name and the window count by querying the application directly. If the application understands the AppleScript command, then if goes to the next item in the appBundleIdentifierList list. If it does not understand, then the window count is calculated by the following:
Attempts to get an invisible window count as they would not show up on the Window menu of an application.
Calculates the window count by the number of windows shown on the Window menu of the application.
Failing these methods it get the window count by querying the application process, with is only accurate fo the active Desktop/Space and is included only for completeness of trying to ascertain the window count just using basic vanilla AppleScript.
Goes to the the application in the appBundleIdentifierList list.
Example AppleScript code:
set menuName to "Window"
tell application id "com.apple.systemevents" to ¬
set appBundleIdentifierList to ¬
the bundle identifier of ¬
(every process whose visible is true)
repeat with appBundleIdentifier in appBundleIdentifierList
try
tell application id appBundleIdentifier to ¬
set {appName, winCount} to {name, (count windows)}
log appName & ": " & winCount & ¬
" -- By querying the application directly."
on error
set winCount to 0
set notVisibleWindowList to {}
set errAppName to ¬
name of application id appBundleIdentifier
tell application id "com.apple.systemevents"
try
tell application process errAppName
set notVisibleWindowList to ¬
(windows whose visible is false)
if notVisibleWindowList is {} then ¬
set winCount to ¬
length of notVisibleWindowList
end tell
end try
try
set theTargetMenuItemsList to ¬
the reverse of ¬
(get name of ¬
menu items of ¬
menu menuName of ¬
menu bar item menuName of ¬
menu bar 1 of ¬
application process errAppName)
on error
set theTargetMenuItemsList to {}
end try
end tell
if theTargetMenuItemsList is not {} then
repeat with anItem in theTargetMenuItemsList
if contents of anItem is ¬
missing value then exit repeat
set winCount to winCount + 1
end repeat
log errAppName & ": " & winCount & ¬
" -- By querying the Window menu of the application process."
else
try
tell application id "com.apple.systemevents" to ¬
set winCount to ¬
(count windows of ¬
application process errAppName)
log errAppName & ": " & winCount & ¬
" -- By querying the application process. " & ¬
"May not be accurate, verify as necessary."
end try
end if
end try
end repeat
Running both versions of the example AppleScript code to show the difference in output:
First version of example AppleScript code:
(*Safari: 6*)
(*TextEdit: 4*)
(*Finder: 3*)
(*BBEdit: 1*)
(*Norton Secure VPN got an error: every window doesn’t understand the “count” message.*)
(*Music: 2*)
(*Sublime Text 2 got an error: every window doesn’t understand the “count” message.*)
(*DiskCatalogMaker got an error: every window doesn’t understand the “count” message.*)
(*Script Editor: 7*)
(*System Preferences: 2*)
(*VMware Fusion got an error: every window doesn’t understand the “count” message.*)
(*Activity Monitor got an error: every window doesn’t understand the “count” message.*)
(*Terminal: 2*)
Second version of example AppleScript code:
(*Safari: 6 -- By querying the application directly.*)
(*TextEdit: 4 -- By querying the application directly.*)
(*Finder: 3 -- By querying the application directly.*)
(*BBEdit: 1 -- By querying the application directly.*)
(*Norton Secure VPN: 0 -- By querying the application process. May not be accurate, verify as necessary.*)
(*Music: 2 -- By querying the application directly.*)
(*Sublime Text 2: 4 -- By querying the Window menu of the application process.*)
(*DiskCatalogMaker: 2 -- By querying the Window menu of the application process.*)
(*Script Editor: 7 -- By querying the application directly.*)
(*System Preferences: 2 -- By querying the application directly.*)
(*VMware Fusion: 1 -- By querying the Window menu of the application process.*)
(*Activity Monitor: 0 -- By querying the Window menu of the application process.*)
(*Terminal: 2 -- By querying the application directly.*)
As you can see even Activity Monitor, a native default macOS application, the Window menu had to be queried as the application directly didn't understand the basic count windows AppleScript command.
Although the output of the second version of the code was accurate across all Desktops/Spaces at the time it was executed, any application that has "By querying the application process. May not be accurate, verify as necessary." as part of its output only includes the window count of the active Desktop/Space it was executed form. The bottom line is using basic vanilla AppleScript there is no guarantee to get a complete accurate window count of every visible application unless all the applications at that time cen be queried directly. Querying the Window menu by its application process should also be accurate.
With that said, I think other methods may need to be used to get an accurate count.

How to get name of frontmost app with AppleScript and use it to get the filepath

What I try to do:
When I'm in one of my text editors (TextEdit, Byword, FoldingText) I want this AppleScript to display the file path.
I figured asking for the frontmost window app get's me the apps name nice and easily and then I can ask for the POSIX path in the next step.
The Problem:
The script is already 99% there, but I'm missing something. When I try to use the variable of activeApp it doesn't work and I get this error:
Error Number:System Events got an error: Can’t get application {"TextEdit"}.
-1728
Here's the script:
tell application "System Events"
set activeApp to name of application processes whose frontmost is true
--This doesn't work either:
--do shell script "php -r 'echo urldecode(\"" & activeApp & "\");'"
tell application activeApp
set myPath to POSIX path of (get file of front document)
end tell
display dialog myPath
end tell
If I exchange activeApp with "TextEdit" everything works. Help would be appreciated.
Maybe there's something in here that helps: Get process name from application name and vice versa, using Applescript
Either get the path property of a document or use System Events to get value of attribute "AXDocument":
try
tell application (path to frontmost application as text)
(path of document 1) as text
end tell
on error
try
tell application "System Events" to tell (process 1 where frontmost is true)
value of attribute "AXDocument" of window 1
end tell
do shell script "x=" & quoted form of result & "
x=${x/#file:\\/\\/}
x=${x/#localhost} # 10.8 and earlier
printf ${x//%/\\\\x}"
end try
end try
The first method didn't work with Preview, TextMate 2, Sublime Text, or iChm, and the second method didn't work with Acorn. The second method requires access for assistive devices to be enabled.
You are asking for...
set activeApp to name of application processes whose frontmost is true
Notice "processes", that's plural meaning you can get several processes in response so applescript gives you a list of names. Even though only one application is returned it's still in list format. Also see that your error contains {"TextEdit"}. The brackets around the name mean it's a list, so the error is showing you the problem.
You can't pass a list of names to the next line of code. As such you have a couple of choices. 1) you can ask for only 1 process instead of all processes. That will return a string instead of a list. Try this code...
set activeApp to name of first application process whose frontmost is true
2) you can work with the list by using "item 1 of the list". Try this code...
set activeApps to name of application processes whose frontmost is true
set activeApp to item 1 of activeApps
Finally, you shouldn't be telling system events to tell the application. Separate those 2 tell blocks of code. Here's how I would write your code.
tell application "System Events"
set activeApp to name of first application process whose frontmost is true
end tell
try
tell application activeApp
set myPath to POSIX path of (get file of front document)
end tell
tell me
activate
display dialog myPath
end tell
on error theError number errorNumber
tell me
activate
display dialog "There was an error: " & (errorNumber as text) & return & return & theError buttons {"OK"} default button 1 with icon stop
end tell
end try
I can't promise the "get file of front document" code will work. That depends on the application. Not all applications will understand that request. That's why I used a try block. In any case though you can be certain you are addressing the proper application. Good luck.
I've been using this snippet for a while, seems to work for all Cocoa apps (not sure about X11):
set front_app to (path to frontmost application as Unicode text)
tell application front_app
-- Your code here
end tell
None of this seems to work with a compiled AppleScript saved as an application and placed on the Dock. Whenever you run the application, IT is the frontmost, not the application that is showing its front window. That application becomes inactive as my Applescript runs. How do I write an Applescript application that isn't active when it runs?
I may have found a solution to the problem listed above. Just tell the user to reactivate the desired application, and give them time.
tell application "Finder"
activate
say "Click front window of your application"
delay 5
set myapp to get name of first application process whose frontmost is true
-- etc.
-- your code
end tell

Listing all windows of all applications

I am trying to write an applescript script that resizes all open windows. In order to make sure that I'm getting to all the windows, I'm making my script say the name of the application as well as the number of open windows of that application.
Interestingly, while I hear the names of all my open applications, my script says that they all have 0 windows open. How can I fix this issue?
Here's my code:
tell application "System Events"
repeat with theProcess in (every process)
if background only of theProcess is false then
if name of theProcess is not "Finder" then
if name of theProcess is "Google Chrome" then
say "Chrome woo hoo"
say (count windows as string)
else
say name of theProcess as string
say (count windows as string)
tell theProcess
repeat with theWindow in windows
say "found a window of"
say (name of theProcess) as string
tell theWindow
click button 2
end tell
end repeat
end tell
end if
end if
end if
end repeat
end tell
I'm on Mac OS X 10.7.5, using automator 2.2.4 to write/run this applescript
You have to tell the process to count windows. After all it's the process that knows about its windows, not system events.
You have told the process to say its name e.g. "say name of theProcess as string" however you only use "say (count windows as string)"... no process is tied to that. Try "count windows of theProcess". Basically you have lines where sometimes you tell the process, other times you don't, and other times where you tell the process even though you've already told the process, so you do it twice. That's where you have "say (name of theProcess) as string" but that code is inside a "tell theProcess" block so it's already being told to theProcess.
Really you need to go through your code and be more precise. A tip... if you want to click a button in a window then the window must be frontmost on the screen otherwise you can't click it. Another tip... "name" is already a string so you don't need to coerce that to a string.
By the way, I agree with Michael Dautermann's comment to your post... there will be processes where you won't get access. But you'll find that out as you progress.
Here's how I would write your code. Basically I would get all of the variables at the beginning using a "tell theProcess" block. Then I can do stuff with those variables. I hope that helps. Notice that I only made the process frontmost which means if it has multiple windows open it will only click a button on the front window. You'll have to add code to make each window come to the front before you can click its button. Good luck.
tell application "System Events"
repeat with theProcess in processes
if not background only of theProcess then
tell theProcess
set processName to name
set theWindows to windows
end tell
set windowsCount to count of theWindows
if processName is "Google Chrome" then
say "Chrome woo hoo"
say windowsCount as text
else if processName is not "Finder" then
say processName
say windowsCount as text
if windowsCount is greater than 0 then
repeat with theWindow in theWindows
say "found a window of " & processName
tell theProcess
set frontmost to true
tell theWindow
click button 2
end tell
end tell
end repeat
end if
end if
end if
end repeat
end tell
I create a list of all open windows of visible applications on Mavericks like this:
tell application "System Events"
set this_info to {}
repeat with theProcess in (application processes where visible is true)
set this_info to this_info & (value of (first attribute whose name is "AXWindows") of theProcess)
end repeat
this_info -- display list in results window of AppleScript Editor
end tell
You need to allow any app using this to access the interface under Accessibility.

Resources