Splitting full-screen apps with AppleScript - macos

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.

Related

Applescript one window resizes, the other doesn't

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?

Why is it reporting "Can't get value" when I ask for the title, not the value?

This is the same code as the application from my last few questions but this version is rewritten to run under "Script Editor" for debugging help.
The error is generated by this code's last line. Checking with Accessibility Inspector, ALL of the menu items have NIL values that is why I specifically reference the title instead.
-- `menu_click`, by Jacob Rus, September 2006
--
-- Accepts a list of form: `{"Finder", "View", "Arrange By", "Date"}`
-- Execute the specified menu item. In this case, assuming the Finder
-- is the active application, arranging the frontmost folder by date.
on menuClick(mList)
local appName, topMenu, r
-- Validate our input
if mList's length < 3 then error "Menu list is not long enough"
-- Set these variables for clarity and brevity later on
set {appName, topMenu} to (items 1 through 2 of mList)
set r to (items 3 through (mList's length) of mList)
-- This overly-long line calls the menu_recurse function with
-- two arguments: r, and a reference to the top-level menu
tell application "System Events" to my menuClickRecurse(r, ((process appName)'s ¬
(menu bar 1)'s (menu bar item topMenu)'s (menu topMenu)))
end menuClick
on menuClickRecurse(mList, parentObject)
local f, r
-- `f` = first item, `r` = rest of items
set f to item 1 of mList
if mList's length > 1 then set r to (items 2 through (mList's length) of mList)
-- either actually click the menu item, or recurse again
tell application "System Events"
if mList's length is 1 then
click parentObject's menu item f
else
my menuClickRecurse(r, (parentObject's (menu item f)'s (menu f)))
end if
end tell
end menuClickRecurse
local destination, libraryName, choices
set destination to "/Users/bryandunphy/music"
set libraryName to "Testing.xml"
tell application "iTunes" to activate
menuClick({"iTunes", "File", "Library", "Export Library…"})
tell application "System Events" to set the value of the text field "Save As:" of window "iTunes" of process "iTunes" to libraryName
tell application "System Events" to tell process "iTunes" to tell its front window's group 1's pop up button 1 to click
tell application "System Events" to tell process "iTunes" to set choices to the title of every menu item of menu 1 of pop up button 1 of group 1 of its front window
repeat with ndx from 1 to count of choices
if the value of choices's item ndx is "" then
tell application "System Events" to tell process "iTunes" to select (the menu item of menu 1 of pop up button 1 of group 1 of its front window whose title is equal to item (ndx - 1) of choices)
end if
end repeat
Because the 'choices' variable contains a list of strings, not a list of menu items, so remove the value of.
You can use the index of the menu instead of the whose clause --> whose title is equal to (....).
repeat with ndx from 1 to count of choices
if choices's item ndx is "" then
tell application "System Events" to tell process "iTunes" to select (menu item (ndx - 1) of menu 1 of pop up button 1 of group 1 of its front window)
exit repeat
end if
end repeat

Why does this Applescript cause "Export Library.scpt: execution error: System Events got an error: Can’t get process "i". (-1728)"?

ƒIt's caused when run through osascript from terminal using the command line "osascript Export\ Library.scpt /Users/bryandunphy/Development/iTunesLibraryConsolidator testing.xml".
same script as my last (solved) question but posting it's entirety this time.
-- `menu_click`, by Jacob Rus, September 2006
--
-- Accepts a list of form: `{"Finder", "View", "Arrange By", "Date"}`
-- Execute the specified menu item. In this case, assuming the Finder
-- is the active application, arranging the frontmost folder by date.
on menuClick(mList)
local appName, topMenu, r
-- Validate our input
if mList's length < 3 then error "Menu list is not long enough"
-- Set these variables for clarity and brevity later on
set {appName, topMenu} to (items 1 through 2 of mList)
set r to (items 3 through (mList's length) of mList)
-- This overly-long line calls the menu_recurse function with
-- two arguments: r, and a reference to the top-level menu
tell application "System Events" to my menuClickRecurse(r, ((process appName)'s ¬
(menu bar 1)'s (menu bar item topMenu)'s (menu topMenu)))
end menuClick
on menuClickRecurse(mList, parentObject)
local f, r
-- `f` = first item, `r` = rest of items
set f to item 1 of mList
if mList's length > 1 then set r to (items 2 through (mList's length) of mList)
-- either actually click the menu item, or recurse again
tell application "System Events"
if mList's length is 1 then
click parentObject's menu item f
else
my menuClickRecurse(r, (parentObject's (menu item f)'s (menu f)))
end if
end tell
end menuClickRecurse
-- Created by Bryan Dunphy during January of 2017
--
-- select the folder "directory" in Window "WinName" of Application "appName" and then clicks the default button if requested
-- if WinName is "" then it uses the frontmost window (to allow for unnamed windows)
-- REQUIRES "on handleDir" and "on findRoot" to work!
-- ONLY call switchDir
-- "createIt" is a boolean that will create any missing directories if it is set to "true".
-- "selectDefault" is a boolean indicating whether or not to click the window's default button after selecting the specified directory
-- returns "true" or "false" to indicate success.
-- clicks "Cancel" button on failure
-- Always returns "true" if "createIt" is set to "true"
on switchDir(directory, winName, appName, createIt, selectDefault)
local dirs, delim
set delim to AppleScript's text item delimiters
set AppleScript's text item delimiters to "/"
set dirs to every text item of directory
tell application "System Events" to tell process appName to set frontmost to true
my findRoot(appName, winName)
repeat with dir in dirs
if not handleDir(dir, winName, createIt) then
tell application "System Events" to tell process appName to tell button "Cancel" to click
return false
end if
end repeat
if selectDefault then keystroke return
return true
end switchDir
on handleDir(dir, winName, appName, createIt)
local foundIt
foundIt = false
local ndx
if winName is not "" then
repeat with ndx from 1 to (count of window winName's list 1)
if window winName's list 1's item ndx's value is equal to dir then
select window winName's list 1's item ndx
foundIt = true
exit repeat
end if
end repeat
else
repeat with ndx from 1 to (count of front window's list 1)
if front window's list 1's item ndx's value is equal to dir then
select front window's list 1's item ndx
foundIt = true
exit repeat
end if
end repeat
end if
if not foundIt then
if createIt then
if winName is not "" then
tell application "System Events" to tell process appName to tell window winName
tell button "New Folder"
click
repeat until window "New Folder" exists
delay 0.5
end repeat
set value of text field 1 of window "New Folder" to dir
tell button "Create" to click
return my handleDir(dir)
end tell
end tell
else
tell application "System Events" to tell process appName to tell its front window
tell button "New Folder"
click
repeat until window "New Folder" exists
delay 0.5
end repeat
set value of text field 1 of window "New Folder" to dir
tell button "Create" to click
return my handleDir(dir)
end tell
end tell
end if
end if
else
return foundIt
end if
end handleDir
on findRoot(appName, winName)
local rootName
if winName is not "" then
tell application "System Events" to tell process appName to tell window winName
tell pop up button 1
click
repeat until menu 1 exists
delay 0.5
end repeat
local ndx
repeat with ndx from 1 to (count of menu 1)
if the title of menu 1's menu item ndx is "" then
set rootName to the title of menu 1's menu item (ndx - 1)
select (menu 1's menu item (ndx - 1))
exit repeat
end if
end repeat
end tell
end tell
else
tell application "System Events" to tell process appName's front window
tell pop up button 1
click
repeat until menu 1 exists
delay 0.5
end repeat
local ndx
repeat with ndx from 1 to (count of menu 1)
if the title of menu 1's menu item ndx is "" then
set rootName to the title of menu 1's menu item (ndx - 1)
select (menu 1's menu item (ndx - 1))
exit repeat
end if
end repeat
end tell
end tell
end if
return rootName
end findRoot
on run (clp)
if clp's length is not 2 then error "Incorrect Parameters"
local destination, libraryName
set destination to clp's item 1
set libraryName to clp's item 2
menuClick("iTunes", "File", "Library", "Export Library…")
set value of parentObject's text field "Save As:" to (libraryName and ".xml")
tell pop up button 1 of group 1 of window "New iTunes Library" of process iTunes of application "System Events" to click
repeat with ndx from 1 to (count of parentObject's menu 1)
if title of menu item ndx is "" then
select menu item (ndx - 1)
exit repeat
end if
end repeat
my switchDir(destination, "iTunes", "iTunes", true, false)
set the value of text field "Save As:" of window "iTunes" to (libraryName + ".xml")
tell button "Save" of window "iTunes" to click
return (destination and "/" and libraryName and ".xml")
end run
Change:
menuClick("iTunes", "File", "Library", "Export Library…")
to:
menuClick({"iTunes", "File", "Library", "Export Library…"})
AppleScript's error reporting is truly awful (no tracebacks, for starters), and osascript is even worse than Script Editor for debugging. If you run the script in SE, it will at least highlight the line in your script where the error occurred. If that doesn't give you a clue, add log commands to report the script's progress. osascript will write logged messages to stderr. In SE, click the awful 'document' (show/hide log) icon at the bottom of the window, then select 'Messages'. If your time is worth more than $100, get yourself a copy of Script Debugger which also lets you add breakpoints and step through and inspect variables as the script runs.

Set a camera in quicktime using applescript

I know there is probably a very simple answer to this. I'm trying to setup an Applescript application that when activated will launch quicktime, open a new movie recording, set the camera and audio, start the recording and then end the recording and save it after 30 seconds.
I currently have a script that is doing everything except setting the camera and audio source. Any tips on how to select a certain camera and audio source using applescript?
Thanks!
Here is the code as it stands now..
--Set some Variables
--Max length of recording
set maxrec to 20 --in seconds
--Ending Message
set EndMsg to "Thank you for participating in this project. Your video has been recorded."
--Begin Loop
repeat
--Get the person's name
repeat
display dialog "What's your name?" default answer ""
set theName to (text returned of result)
if theName ≠ "" then exit repeat
end repeat
--Set the date
set theDate to current date
set y to text -4 thru -1 of ("0000" & (year of theDate))
set m to text -2 thru -1 of ("00" & ((month of theDate) as integer))
set d to text -2 thru -1 of ("00" & (day of theDate))
set h to text -2 thru -1 of ("00" & (hours of theDate))
set mm to text -2 thru -1 of ("00" & (minutes of theDate))
set dateStamp to (y & "-" & m & "-" & d & " " & h & mm as string)
--Create a folder for the person
tell application "Finder"
set p to path to movies folder from user domain
try
make new folder at p with properties {name:"Video Clips"}
end try
set p to (path to movies folder from user domain as text) & "Video Clips:"
make new folder at p with properties {name:dateStamp & " " & theName}
set thePath to result
--Establish the final name of the movie
set fileName to ((thePath as text) & dateStamp & ".mov" as string)
end tell
--Open New Recording, start and stop it
tell application "QuickTime Player"
set newMovieRecording to new movie recording
set windowID to id of first window whose name = "Movie Recording"
delay 2
tell newMovieRecording
--Set Camera to Blackmagic
set current camera of newMovieRecording to Blackmagic of video recording devices
start
end tell
set theSeconds to maxrec
repeat theSeconds times
display dialog theSeconds buttons {} giving up after 1 with title "REMAINING TIME"
set theSeconds to (theSeconds - 1)
end repeat
tell newMovieRecording
stop
end tell
--save and close the recording
set newMovieRecordingDoc to first document whose name = (get name of first window whose id = windowID)
tell newMovieRecordingDoc to save in fileName
set SavedRecordingDoc to first document whose name = (get name of first window whose id = windowID)
tell SavedRecordingDoc to close
quit
end tell
--Display "Thank You" Message
tell application "System Events" to set frontmost of process "Video Booth" to true
display dialog EndMsg giving up after 20 buttons {"Continue…"} ¬
default button 1
end repeat
Here is the error I get right now when I run it.
error "QuickTime Player got an error: Can’t make Blackmagic of every video recording device of document \"Movie Recording\" into type specifier." number -1700 from Blackmagic of every video recording device of document "Movie Recording" to specifier
use the current camera and current microphone properties of your movie recording object. Just replace item 1 with the appropriate device:
set current camera of recording to item 1 of video recording devices
set current microphone of recording to item 1 of audio recording devices
I had accomplished this by doing it manually
tell application "System Events" to tell process "QuickTime Player"
#To open dialog to show available cameras
click button 3 of window 1
#To select our device
click menu item "your_camera_name" of menu 1 of button 3 of window 1
end tell
With this the dialog will open and you will select your camera from the list...
Taken from another answer I made:
https://stackoverflow.com/a/45454785/3685973

Show context menu on Mac with keyboard using AppleScript and Automator

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

Resources