I am trying to write an apple script that when executed, will make the currently focused window "always on top". Is this possible?
In objective-c I can do this on windows my process owns by using [NSWindow setLevel:] but my struggle is to do it for windows my process does not own. So I am now trying to do it via apple script.
I tried this:
global frontApp, frontAppName, windowTitle
delay 3
set windowTitle to ""
tell application "System Events"
set frontApp to first application process whose frontmost is true
set frontAppName to name of frontApp
tell process frontAppName
set level of window 1 to 3
tell window 1
set windowTitle to value of attribute "AXTitle"
end tell
end tell
end tell
return {frontAppName, windowTitle}
However this gives me error -
System Events got an error: Can’t make level of window 1 of process "firefox" into type specifier.
Related
Im trying to do some manipulation with preview using Automator. I have several windows open in preview, two of which are of interest. One is named "Markup Badges.png" one is called "Screenshot.png"
I want to set the focus to "Screenshot.png", copy the image and close the window, then I want to close the "Markup Badges.png" window.
I am having a lot of trouble getting this to work.
As part of my experimenting I have created two scripts trying to get the windows in focus, so I can then perform additional actions on them
Script 1:
on run {input, parameters}
tell application "Preview"
set visible of every window whose visible is true to false
end tel
tell application "Preview"
try
set theWindow to 1st window whose name begins with "Screenshot.png"
set index of theWindow to 1
activate
end try
end tell
-- stuff
delay 1
tell application "Preview"
set visible of every window whose visible is false to true
end tell
return input
end run
Script 2:
on run {input, parameters}
tell application "Preview"
set visible of every window whose visible is true to false
end tell
tell application "Preview"
try
set theWindow to 1st window whose name begins with "Markup Badges.png"
set index of theWindow to 1
activate
end try
end tell
-- stuff
delay 1
tell application "Preview"
set visible of every window whose visible is false to true
end tell
return input
end run
If I run Script 1 on its own, it does what I expect, Screenshot.png window is shown and has focus.
If I run Script 2 on its own, it does what I expect, Markup Badges.png window is shown and has focus.
If I run both scripts (play button in automator, Script 1 runs, followed by script 2) then script 2 does not work as it does on its own. The window is shown, but it does not have focus meaning I cannot send any keypresses to the window.
Any help appreciated
I found changing the code to the below fixed the problem.
on run {input, parameters}
tell application "Preview"
set visible of every window whose visible is true to false
end tell
tell application "Preview"
try
set theWindow to 1st window whose name begins with "Markup Badges.png"
set index of theWindow to 1
activate
tell application "System Events" to tell process "Preview" to perform action "AXRaise" of window 1
end try
end tell
-- stuff
delay 1
tell application "Preview"
set visible of every window whose visible is false to true
end tell
return input
end run
This script works when terminal application(or any application) is not in FullScreen mode. It will return the correct value of false. When you make the application fullscreen it returns any empty value. The only thing I can think of is that when you go fullscreen it puts it on a different desktop? Am I missing something to activate the app differently now that it is fullscreen?
tell application "System Events" to set the visible of every process to true
set white_list to {"Finder", "AppleScript Editor", "Google Chrome"}
tell application "System Events"
set process_list to the displayed name of every process whose visible is true
set process_number to (number of items in process_list)
set myList to process_list
end tell
repeat with theItem in myList
if theItem is not in white_list then
log theItem
tell application "System Events" to tell process theItem
set isFullScreen to the value of attribute "AXFullScreen" of windows
end tell
end if
end repeat
return theItem & " application is FullScreen: " & isFullScreen
This line won't work as written...
set isFullScreen to the value of attribute "AXFullScreen" of windows
"windows" will return a list of windows and you can't get that attribute from a list. So you would want to write it as...
set isFullScreen to the value of attribute "AXFullScreen" of window 1
However, with that said I tried this and it seems you can't get the windows from a full screen process. The list of windows is always empty {}. So this approach of determining if an application is full screen will not work.
You'll need to think of another way to figure out if an application is full screen. I tried a couple things and couldn't find a solution. Sorry.
I've seen a lot of posts for how to send a window to the front in applescript, but I want to be able to send it to the back. How do I write an applescript that will do this?
Maybe you don't actually need to move any windows. Maybe you can just hide your application so your window isn't showing. Since you don't want your window on the top then it's probably OK to just hide your application. It continues running and does its thing but its window doesn't cover any other windows.
Just change "Safari" to the name of your application.
set myAppName to "Safari"
tell application myAppName to activate
tell application "System Events"
-- wait until your application comes forward and then hide it
repeat
set p to first process whose frontmost is true
if name of p is myAppName then
set visible of p to false -- hide your application
exit repeat
end if
delay 0.2
end repeat
end tell
EDIT: if hiding your app doesn't work then you could just keystroke command-tab which is the application switcher command. Basically your app will come to the front and then the keystroke will make the previously frontmost application come to the front. So your window won't go all the way back but it won't be in the front. Maybe that will work.
set myAppName to "Safari"
tell application myAppName to activate
tell application "System Events"
-- wait until your application comes forward
repeat
set p to first process whose frontmost is true
if name of p is myAppName then exit repeat
delay 0.2
end repeat
-- use the application switcher to bring the previously frontmost application forward
keystroke tab using command down
end tell
Something like set index to 999 doesn't seem to work, but set index to (count windows) does:
tell application "TextEdit"
set index of window 1 to (count windows)
end tell
You might also raise all other windows:
tell application "System Events" to tell process "TextEdit"
repeat with w in windows 2 thru -1
perform action "AXRaise" of w
end repeat
end tell
This will move the front finder window to the back...
tell application "Finder" to set index of front Finder window to (count Finder windows)
I have not used "openFrameWorks" so I am not sure of how it works…
But rather than reinvent the wheel with Applescript.
Can you not set the window level in "openFrameWorks"
In xcode/Objective - c I would use the NSWindow Window Levels constants.
To set a normal window:
[awindow setLevel: NSNormalWindowLevel];
But set a window below other normal windows:
[awindow setLevel: NSNormalWindowLevel - 1000];
This will insure the window is always below any normal applications windows. Even when I click on it or drag it. It stays behind other windows.
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.
I have an application with several windows opened at the same time.
I'd like to bring a specific window to foreground (I know its title).
At the moment I'm using a combination of keys to achieve this task but I'd like to try something different since I'm experiencing some problems with this approach.
tell application "System Events"
set frontmost of process "appIT" to true
keystroke "1" using command down
delay 0.2
end tell
This is possible by using the "AXRaise" action, except on certain window (applications that use X11 for example).
Try this.
set theTitle to "some title"
tell application "System Events"
tell process "appIT"
set frontmost to true
perform action "AXRaise" of (windows whose title is theTitle)
end tell
end tell
If your application is scriptable and allows setting the index of a window, you can do the following (based on an answer in How do I make a Safari window active using AppleScript (elegantly)?)
to raiseWindow of theApplicationName for theName
tell the application named theApplicationName
activate
set theWindow to the first item of ¬
(get the windows whose name is theName)
if index of theWindow is not 1 then
set index to 1
set visible to false
set visible to true
end if
end tell
end raiseWindow
The toggling of the visibility is necessary to deal with some weirdness that occurs with switching applications. If you don't toggle the visibility, the window won't be the first when you switch away from and back to the application. Unfortunately, this toggling shrinks the window to the dock then restores it, a very dramatic UI disruption.
Here's another way I've found to deal with the weirdness:
to raiseWindow2 of theApplicationName for theName
tell the application named theApplicationName
activate
set theWindow to the first item of ¬
(get the windows whose name is theName)
if the index of theWindow is not 1 then
set the index of theWindow to 2
tell application "System Events" to ¬
tell application process theApplicationName to ¬
keystroke "`" using command down
end if
end tell
end raiseWindow2
I don't think System Events can change the front window of a process. Of course you can close the front window until the window you want is on top. That's not really a solution though as you probably don't want to close windows. Really though the only way you could achieve this is if the application itself is apple-scriptable and allows you to do this.