Automator - AppleScript recording and changing into values - macos
I'm recording an Automator Script to help me scrape an URL that I use to get information from a week at a time. I would like to specific value to be a vaiable.
When I have recorded the script I would like to have the specific values (i.e. 2018-01-02) be able to change into a variable that can be changed within the script (i.e. a day at a time to 2018-01-03). I can not change the date to a variable in Automator. If I copy the script to a text document I can not get it back into Automator.
Is there a way to get this done?
The script as text looks like this:
-- Click the “2018-01-02” button.
delay 2.631525
set timeoutSeconds to 2.000000
set uiScript to "click UI Element \"2018-01-03\” of group 7 of UI Element 1 of scroll area 1 of group 23 of UI Element 1 of scroll area 1 of group 1 of group 1 of tab group 1 of splitter group 1 of window \"Historical Data Feed\" of application process \"Safari\""
my doWithTimeout( uiScript, timeoutSeconds )
in Automator it looks like:
Click the "2018-01-02" button
#Matts gave a fine answer about using variables in Automator. But, having discussed the nature of your objective in more depth, it's clear that knowing how to reference variables in Automator is not at all the issue, and won't help you.
In fact, don't even bother with Automator. It's not going to do what you want it to. The whole endeavour of trying to automate physical interaction with Safari means it's going to involve a fair bit of AppleScripting, and it's the worst kind—GUI (graphical user interface) scripting.
Come out of Automator and use Script Editor. Make sure Script Editor has relevant accessibility permissions in order for GUI scripting to work. You can do this by going to System Preferences > Security & Privacy > Privacy > Accessibility, unlocking the padlock in the bottom left of the window, and then finding Script Editor in the list of apps allowed to control your computer. Make sure it's checked.
Briefly, GUI scripting is pretty much doing just that, in a nutshell: controlling your computer, in a way that allows it to issue mouse clicks and press buttons or menu items as if the user were there physically doing it themselves.
But it's a hell of a nightmare figuring out how to reference objects in the GUI hierarchy of on-screen elements that can be pressed or clicked.
I think your snippet of Automator-manufactured code was misleading you:
click UI Element "2017-01-03” of group 7 of UI Element 1 of scroll area 1 of ¬
group 23 of UI Element 1 of scroll area 1 of group 1 of group 1 of tab group 1 ¬
of splitter group 1 of window "..." of application process "Safari"
I think you saw UI Element "2017-01-03" and deduced that one must be able to access date-specific data by substituting the appropriate date string in for the one that's there currently. Hence, why you asked about using variables in Automator. But it wouldn't have worked. That date string just reports the name of the element (which happens to be something called a static text element) that contained a piece of text with a date in it, but not one that was editable. Therefore, referencing UI Element "2017-01-02" would just throw an error, because no such element exists.
So here's what I've come up with so far. It's by no means a full package: I haven't written the script that will achieve your objective straight out of the box, but I'm definitely providing you with all the necessary components for you to be able to piece together a finished product. Believe me, I did the most grueling part of it, which a painstaking search to obtain all the specific object references for every button you will want to be able to click within the GUI hierarchy.
First, an important note: I didn't use the URL you posted in the comments to do my GUI inspection. The webpage was far too busy and convoluted, which would have made the job much harder. But luckily, they had a link to a page that contains only the widget with which we'll be interacting. But, because the GUI makeup of the page is different (the widget is identical to the one your original link, it's just the rest of the page that's different), none of the code I've written will work with your original link. So you'll have to use the URL that I've provided in the script, which is https://www.dukascopy.com/plugins/fxMarketWatch/detach.php?id=ct23.
Referencing the GUI objects
Declare some property variables, which you can change according to what month and year you want to retrieve the datasets from. You can also specify your instrument, by either the short abbreviation code, or its full description:
property |URL| : "https://www.dukascopy.com/plugins/fxMarketWatch/detach.php?id=ct23"
property Instrument : {|short|:"AAPL.US/USD", |description|:"APPLE INC"}
property |month| : "December"
property |year| : "2017"
This next part is where System Events and the Safari process are employed, which is the root of the GUI scripting commands. I also lumped in with it a reference to all the elements that were common to all ones we'll be interacting with. Otherwise, you'd have to write them out over and over, whereas it's easier to just reference them once as a group of UI elements that form the basis of a tell... block:
tell application "System Events" to ¬
tell application process "Safari" to ¬
tell (window named |URL|) to ¬
tell UI element 1 of scroll area 1 of group 1 of ¬
UI element 1 of scroll area 1 of group 1 of ¬
group 1 of tab group 1 of splitter group 1
.
.
(* The rest of our code goes
inside this tell block *)
.
.
end tell
Note also that the commands are being sent to the window that's named the same as the URL of the webpage. So this script assumes that the webpage has already been loaded up in Safari—it doesn't load it for you (although that wouldn't be difficult to implement).
Also make sure you log in after you load the page. If you don't, clicking the download button brings up the login box, which will interrupt the whole script and throw an error. But, once you're logged in and click the box to "stay logged in", from then on it will proceed to the download options by itself.
The next part is the meat of the endeavour. This is where I define variables, each of which reference a GUI object that you will want to be able to issue clicks and presses to from the script.
These three form the main components for each row of available instruments that you can select:
set Instruments to a reference to groups of group 1 of group 1 of group 3
--> All Instruments (410 of them)
set Instrument_Abbreviations to a reference to static texts of group 1 of group 2 of Instruments
--> The short text code for each instrument
set Instrument_Descriptions to a reference to static texts of group 2 of Instruments
--> The long text for each instrument
This one is the pair of elements for selecting the timezone, local or GMT:
set LOCAL_GMT to a reference to checkboxes of group 1 of group 7 of group 4
This next block of elements took some work to navigate through. These are the ones responsible for selecting the date. Pressing the date button allows a calendar pop-up to appear on-screen, in which you choose the year, month and day. This will also form the core of what will become your program loop, as you use the variables to cycle through the days of a month to retrieve the data files for all of, say, December.
The thing about menus and objects such as this calendar that "pop up" is that they don't physically exist until the moment they are created and appear on our screens. Therefore, there's potential in GUI scripting for errors to get thrown when the script tries to reference an object that will be there at some future point, but is not currently there and doesn't exist yet. Using a reference to is one way to cater for this problem, and I've done this for most of these variable declarations.
set Date_button to a reference to button 1 of group 3 of group 4
--> The date button
set Calendar_year_button to a reference to button 2 of UI element 2 of row 1 of table 1 of group 5
--> The button that pops up the year menu (as shown)
set Calendar_year_list_items to a reference to static texts of groups of group 1 of group 1 of UI element 1 of row 1 of table 1 of group 5
--> The year menu
set Calendar_month_button to a reference to button 2 of UI element 1 of row 1 of table 1 of group 5
--> The button that pops up the month menu
set Calendar_month_list_items to a reference to static texts of groups of group 1 of group 1 of UI element 1 of row 1 of table 1 of group 5
--> The month menu
set Calendar_days to a reference to static texts of UI elements 2 thru -1 of rows 3 thru -2 of table 1 of group 5
--> All the day text objects visible at any one time
--> 6 rows x 7 days = 42 text objects
The next three variables are for clicking the "Tick" button, which brings up a pop-up menu. Last in this group are the pair of BID/ASK buttons.
set Tick_button to a reference to static text 1 of group 1 of group 1 of list 2 of group 1 of group 4
--> The tick button
set Tick_menu_items to a reference to menu items of list 1
--> The pop-up menu items as shown below
set BID_ASK to a reference to checkboxes of group 1 of group 2 of group 4
--> The "BID" and "ASK" buttons
Next is the download button:
set Download_button to a reference to button "Download" of group 8 of group 4
Now, when the download button is clicked, the GUI goes under a reconstruction in this bottom section. Firstly, a progress bar is shown in place of all the buttons that temporarily vanish:
I ignored these elements as I didn't see any need to have access to the cancel button in such a short space of time.
Then another reconstruction and the "Save As CVS" and "Reset" buttons come into existence. We need both:
set Reset_button to a reference to button "Reset" of group 2 of group 4
set Save_button to a reference to button "Save as .csv" of group 2 of group 2 of group 4
And now we have all the GUI objects we need access to, stored by reference in a set of variables that we (by which I mean you) can make use of to carry out the actual process of automation.
Performing Some Actions
Some of the variables contain references to elements that can perform actions straight out of the box, e.g. click Date_button. However, others will throw an error or not perform quite as you expect them to. This is because some of the variables contain a reference to a single object, such as Date_button, whilst others contains references to several different objects or groups of objects, in a nested list, such as Calendar_year_list_items.
Before utilising these nested lists, we have to flatten them into a single list. The following function will do this, and can be added at the very end of the script (after the end tell:
on flattened(L) -- recurse nested lists and return a one-dimensional list
if class of L is not list then
return [L]
else if length of L is 0 then
return L
else
return flattened(the first item of L) & ¬
flattened(the rest of L)
end if
end flattened
Now we have everything we need to start getting these objects to perform some actions. For this, I think it's best to write another function. It can just before the on flattened function declaration:
on Download_CSV_Data for _instrument from _date
local _instrument, _date
set [_day, _month, _year] to [day, month, year] of date _date
global Instruments, Instrument_Abbreviations, Instrument_Descriptions, LOCAL_GMT, ¬
Date_button, Calendar_year_button, Calendar_year_list_items, Calendar_month_button, ¬
Calendar_month_list_items, Calendar_days, Tick_button, Tick_menu_items, ¬
BID_ASK, Download_button, Reset_button, Save_button
tell application "System Events"
click 1st item of my flattened(Instrument_Abbreviations whose name is _instrument)
-- click 1st item of my flattened(Instrument_Descriptions whose name is Instrument's |description|)
click Tick_button
click (Tick_menu_items whose name is "Second")
click (BID_ASK whose name is "ASK")
click (LOCAL_GMT whose name is "GMT")
click Date_button
click Calendar_year_button
click 1st item of my flattened(Calendar_year_list_items whose name is _year)
click Calendar_month_button
click 1st item of my flattened(Calendar_month_list_items whose name is (_month as text))
click 1st item of my flattened(Calendar_days whose name is _day)
click Download_button
repeat while not (exists Reset_button)
delay 1
end repeat
click Save_button
click Reset_button
end tell
end Download_CSV_Data
In order to download a data file pertaining to a specific date, all you have to do is call that function. You would place that function call between the first end tell statement (after the variable declarations), ad before the first function declaration, so your script will look something like this:
property |URL| : "https://www.dukascopy.com/plugins/fxMarketWatch/detach.php?id=ct23"
property Instrument : {|short|:"AAPL.US/USD", |description|:"APPLE INC"}
property |month| : "December"
property |year| : "2017"
-- GUI object variable declarations
tell application "System Events" to ¬
tell application process "Safari" to ¬
tell (window named |URL|) to ¬
tell UI element 1 of scroll area 1 of group 1 of ¬
UI element 1 of scroll area 1 of group 1 of ¬
group 1 of tab group 1 of splitter group 1
.
.
.
end tell
(*** Insert function calls here ***)
Download_CSV_Data for Instrument's |short| from "17/12/2017"
on Download_CSV_Data for _instrument from _date
.
.
.
end Download_CSV_Data
on flattened(L) -- recurse nested lists and return a one-dimensional list
.
.
.
end flattened
If you're in America, you might have you switch the month and day around in the short date. So where I've written Download_CSV_Data for Instrument's |short| from "17/12/2017", you might have to write Download_CSV_Data for Instrument's |short| from "12/17/2017" (use whatever short-date format your computer system uses).
You can set variables in Automator, by using the "run applescript" command. Search for the command by typing "applescript" into the search box in automator. Then you can set the variable within the script. Here's an example of how it could be done:
set dateVar to "2018-01-02"
-- Click the “2018-01-02” button.
delay 2.631525
set timeoutSeconds to 2.0
set uiScript to "click UI Element \"" & dateVar & "\" of group 7 of UI Element 1 of scroll area 1 of group 23 of UI Element 1 of scroll area 1 of group 1 of group 1 of tab group 1 of splitter group 1 of window \"Historical Data Feed\" of application process \"Safari\""
my doWithTimeout(uiScript, timeoutSeconds)
You could also use other methods of setting the date. This article has a lot of information about using dates with Applescript https://macscripter.net/viewtopic.php?id=24737
The UI element numbers can also vary, so you would need to account for this to get your script to work with variables.
Edit: As you're getting assistance to use another method, I can see how my answer wouldn't be relevant to your situation now. Although Applescript will get the job done, it's quite a difficult way to accomplish the end goal of downloading the files. I've found that using Selenium, Firefox and Python to be a far easier way to automate the browser to download files. It works best when you understand how to use xpath, and occasionally execute some javascript, but if it's something you're doing more of in the future, you might want to consider this method. You can also use tag, id or class selectors instead of xpath.
Related
Applescript- UI Element Group changes when I scroll around page
I'd like to preface that I am very new to Applescript and UI scripting. I am working inside a proprietary CMS and wish to build a script to open a file search box, paste in the filename, select it, and click Finish. I have been successful scripting the UI hierarchy, but have noticed that if I scroll around the page then the Group number changes which breaks my script. For example, this snip of script works, but if I scroll around the page then the Group number changes from 47 to 48. The entire script is performing several copy/paste functions and this Group change sometimes occurs mid-run, so I can't "set it and forget it". tell application "System Events perform action "AXShowMenu" of text field 1 of group 1 of group 1 of group 1 of group 47 of UI element 1 of scroll area 1 of group 1 of group 1 of tab group 1 of splitter group 1 of window "Strapi - Content Manager" of application process "Safari" end tell I'd like to target this text field via an UI attribute (and hopefully ignore Group number or possibly condense the UI string), but am not sure exactly what to use. Several fields such as Title and Description are blank. I am including a screenshot of UI Browser for this element. I'm also open to incorporating JavaScript but have zero knowledge so there'd be an obvious learning curve there. I appreciate any and all help!
How to identify WinEdit and WinComboBox objects embedded in a WinListView control in UFT?
I'm automating tests for a desktop application on Windows platform. I'm using HP UFT and VBScript. In a WinListView control which is used to display a multi-valued list(Each row in the list has four columns in it). After hovering on each row to select it, when mouse click operation is performed on any of the four cells the respective control in that cell shows up(an editbox or a dropdown list) It seems that the developers have embedded temporary WinEdit and WinComboBox controls in the cells of each row which makes it difficult to use Object spy on them. I succeeded in getting object spy to work on those by holding control key, but the properties of those objects are not unique, for instance ComboBox in the third cell in different rows has the same properties.Posting ObjectSpy results here. **ComboBox in Row 1 Cell 3** "Class Name:=WinComboBox", "abs_x:=1041", "abs_y:=551", "all items:=Hours\nMinutes", "attached text:=", "enabled:=True", "focused:=True", "height:=21", "hwnd:=7602202", "items count:=2", "nativeclass:=ComboBox", "object class:=ComboBox", "regexpwndclass:=ComboBox", "regexpwndtitle:=Minutes", "rightaligned:=False", "righttoleftlayout:=False", "righttoleftreading:=False", "selection:=Minutes", "text:=Minutes", "visible:=True", "width:=90", "window id:=7988488", "windowextendedstyle:=0", "windowstyle:=1342177987", "x:=354", "y:=257" **ComboBox in Row 5 cell 3** "Class Name:=WinComboBox", "abs_x:=1041", "abs_y:=619", "all items:=Hours\nMinutes", "attached text:=", "enabled:=True", "focused:=True", "height:=21", "hwnd:=11601852", "items count:=2", "nativeclass:=ComboBox", "object class:=ComboBox", "regexpwndclass:=ComboBox", "regexpwndtitle:=Minutes", "rightaligned:=False", "righttoleftlayout:=False", "righttoleftreading:=False", "selection:=Minutes", "text:=Minutes", "visible:=True", "width:=90", "window id:=7988488", "windowextendedstyle:=0", "windowstyle:=1342177987", "x:=354", "y:=325" I have tried selecting rows using WinListView.GetItem but that gives hold of the first cell only which is an editbox and it works. For the remaining three columns I can't find any workaround. I have tried performing clicks using WinListView.GetItem().Select operation providing offset in pixels to click on each cell but that doesn't activates the embedded controls although I see the mouse moving and performing clicks when the test runs. Record and play didn't provide much insight either as the play operation doesn't replicate the actions successfully every time and uses same and generic names for actions on cells of different rows. WinListView("WinListView Name").WinEdit("Edit").Set "20" DialogObject.WinComboBox("ComboBox").Select "Hours" Image 1 The basic WinListView looks like this Image 2 After selecting a row and clicking on a cell the ComboBox or EditBox appears
Detect order of applications, windows, tabs
I'm using AppleScript to find all tabs in multiple browsers (testing on Safari first) with certain criteria in it's title and give it to stdout for another script. So I have the basic information I need; window id tab index tab name tab visible So from this point I know which of my Safari screens are matching my criteria and I log their window id and their tab index. Besides that with tab visible I can know which is the foremost one. Now I still have one issue. I really want to be able to know which window and tab was the last one active. Even if I can only know the window id that was used last by the user it would automatically mean that inside that window the tab with visible true is the last one. But there is one more thing. If the visible tab is not meeting my criteria, then I would still need to know the order of the last active one too. So what I'm looking for is an counter/order value of the last active windows and tabs. I can't find something in the documentation that could give me that counter. For example; the TAB-logic in OS X knows which apps were last used. I was wondering if that logic would be available as some kind of system variable and then also on it's window/tab sub level. My code (slimmed down does this): tell application "Safari" ... repeat with win in winlist ... repeat with t in tablets # win.id # t.index # t.name # t.visible end repeat end repeat end tell And so I'm looking for something that emulates win.lastUsedOrderIndex and t.lastUsedOrderIndex.
The simple answer is that if you do not find the properties you need in the application's dictionary, then you are out of luck. Window and Document lists in AppleScript are normally in a front-to-back ordering, since they are based on the orderedWindows and orderedDocuments NSArrays. Tabs in a browser are probably ordered left-to-right or right-to-left, based on the language localization, but I would be surprised if any browser had a reason to return tabs ordered by when they were "last used", whatever that means.
Automator to Applescript for Editing Code
Just wondering if I can convert a "watch me do" event in Automator to applescript and then edit the resulting code? I've got a recording of entering a query (i.e. Apple1) into Google, but I'd like the query to increase ++ for each loop of the recording, so the result is Apple1, then the next loop would be Apple2, Apple3, etc. I know increments are elementary in programming, but I'm just not sure how to do it using Automator and/or applescript. Any help would be greatly appreciated.
A few years late but yes you can. How to convert a list of Watch Me Do events to AppleScript: 1. Record the Watch Me Do actions 2. In the Watch Me Do window select all of the events you want to edit 3. Drag those events to a blank position below the Watch Me Do window (where you would place another Automator action) 4. Edit! --------------------------------------------------See Below for Images-------------------------------------------------- NOTE: In Step 3, when dragging the actions to a blank space you will see a green circle icon with a white plus. Sorry I can't find the exact icon but here is one that looks close enough to give you the idea. STEP 1 STEP 2 STEP 3 STEP 4
It may be simpler just doing it all in Applescript. Example: set counter to 0 set theTerm to "apple" repeat 4 times set counter to counter + 1 open location "http://www.google.co.uk/#hl=en&output=search&sclient=psy-ab&q=" & theTerm & counter end repeat Running this code will have your browser open a new search for apple1 through 4 Update: Response to Comment request: This example shows you how to search google images and randomise the search using different terms and numbers. set theTerms to {"apple", "orange", "banana", "pear"} set termCount to count of theTerms set searchLoc to "http://images.google.com/search?hl=en&site=&tbm=isch&source=hp&biw=2171&bih=1062&q=" repeat 4 times set termRandNumber to random number from 1 to termCount set randomSuffixNumber to random number from 1 to 7000 open location searchLoc & item termRandNumber of theTerms & randomSuffixNumber end repeat
OSX Lion AppleScript : How to get current space # from mission control?
I'm trying to figure out how to get the current space # from mission control. Source would be helpful, but more helpful would be info on how to figure this out myself. I've written a few applescripts, but more often than not it seems like any time I need to do something new (that I can't find dictionary documentation for) it falls under the category of "tell this specific app (e.g. "System Events") this very specific thing" and I've no clue how I would actually figure that out. Specifically what I am trying to do: I hate the new mission control in OSX 10.7. I want my spaces "grid" back since I used it all the time. I used to navigate between spaces using arrow keys (e.g. ALT+↑) every few seconds. Now I'm stuck with this clunky 1x9 array of spaces instead of an elegant 3x3 grid. I've re-mapped all my spaces to use the number pad, which partially takes care of the problem (since it is a 3x3 grid), but only when I have an external keyboard attached. Basically, I want to be able to use ALT+↑ and ↓ again, but to do so I need to detect the current space # so that I can switch from space 5-->2, for example. Dave's answer below, although far more detailed than I expected, requires writing an app to do this (plus it still doesn't fully answer the question). If it's at all possible, I'd rather just bind a few keys to an applescript.
I'm trying to figure this out myself. Not there yet, but in the right direction: Each Mission Control "space" gets a uuid assigned to it... ...except for the very first one (AFAIK), and the Dashboard one. You can read them here: $ defaults read com.apple.spaces $ defaults read com.apple.desktop File locations: ~/Library/Preferences/com.apple.spaces.plist ~/Library/Preferences/com.apple.desktop.plist Here's mine. I have four spaces enabled, and three entries show up: $ defaults read com.apple.spaces { spaces = ( { type = 0; uuid = "9F552977-3DB0-43E5-8753-E45AC4C61973"; }, { type = 0; uuid = "44C8072A-7DC9-4E83-94DD-BDEAF333C924"; }, { type = 0; uuid = "6FADBDFE-4CE8-4FC9-B535-40D7CC3C4C58"; } ); } If you delete a space, that entry will get removed from the file. If you add a space, an entry will be added. Again, there's never an entry for Desktop 1 or Dashboard. I'm not sure if there's a public API to figure out what space uuid is being displayed on a display. I'd assume that no uuid means Display 1, and the others' mean Display 1+n. I took a quick glance through the AppleScript Editor Library (Window ---> Library) and didn't see any entries under System Events for spaces. This is probably something that can be done with Cocoa, perhaps via private API, but I'm not sure about AppleScript. UPDATE - July 23, 2011 It looks like Dock controls Mission Control. You can grab its header files like so: Go to: /System/Library/CoreServices/Dock Right-Click and Show Package Contents Navigate: /Contents/MacOS/ Copy and paste the Dock binary to your desktop. Run: $class-dump ~/Desktop/Dock That'll spit out all of its header files (it's long; almost 7,500 lines). You can see the spaceUUID strings appearing in there. There's a class called WVSpace which appears to represent a single Space in Mission Control, and a lot of other WV* classes. I'll keep looking at it tomorrow; too tired now. :) UPDATE - July 24, 2011 Inside Dock there's a class called WVSpaces. It has a number of attributes including: WVSpace *currentSpace; unsigned int currentWorkspace; WVSpace *nextSpace; // Space on the right??? WVSpace *previousSpace; // Space on the left??? BOOL currentSpaceIsDashboard; BOOL dashboardIsCurrent; ...lots more... Each WVSpace class has an NSString *_uuid; attribute, which is likely its SpaceUUID. So theoretically you can get the current space number like so: WVSpace *currentSpace = [[WVSpaces sharedInstance] currentSpace]; NSString *currentSpaceUUID = [currentSpace _uuid]; // Empty string if main space??? The trick is, how to get access to the private WVSpaces class buried inside of Dock? I'm assuming it's Singleton as it has an NSMutableArray *_spaces; attribute, probably with every space listed in it. Only one space gets displayed at a time (this holds true if you're using multiple monitors; the space spans across both of them), so it makes sense to only have one WVSpaces instance. So it looks like it'll require some SIMBL hacking of Dock to gain access to WVSpaces.
I've been poking around, and I came up with this: https://gist.github.com/1129406 Spaces have a nonsequential ID and a sequential index (0-based). You can get the ID in two ways: from public APIs (see get_space_id) from the private CGS API CGSGetWorkspace You can set the current space by index using public APIs (though the notifications themselves are not publicly documented): see set_space_by_index You can set the current space by ID using private the CGS API CGSSetWorkspace. You cannot get the current space index directly. However, if you're always using the same set of nine spaces, you can rotate through them once using set_space_by_index, collect their IDs, and build a mapping. Then you will be able to get the current index from the ID.
... also been working on this :) You say that you "need to to detect the current space #". This is not strictly true: To move down one row, you just move 3 spaces right, so in principle you could just bind something like tell application "System Events" to tell process "WindowServer" key code {124, 124, 124} using control down end tell to Alt-down (with FastScripts, Alfred or some other fast method that avoids the overhead of Automator). This approach will fail if you ever hit down in the bottom row, of course -- but if you are truly hard-wired, you never do :) You have to "Enable access for assistive devices" in the Universal Access preference pane for the key code approach to work. Caveat: This doesn't work. When I launch the script above, I nicely jump three spaces. The problem is that afterwards my keyboard goes unresponsive: It seems that only the window manager is receiving events: I can close windows and switch space, but I cannot interact with any applications. My theory is that this happens when the jump causes the current application to change during the execution of the script -- but I have no idea how to fix this. A related observation: The Mission Control (i.e. /Applications/Mission Control.app/Contents/MacOS/Mission\ Control) seems to react to some command line arguments: Mission\ Control: show mission control Mission\ Control 1: show desktop Mission\ Control 2: show current application windows I tried putting in some of the UUID's from defaults read com.apple.spaces, but that didn't do much. So much for fumbling in the dark.
I wrote an app - does it work for you? Change Space.app The keys to make it work are control-shift and the arrow keys, although this may be fixable if you are stuck on ALT. Make sure you have 9 spaces (desktops) set up before you start, and you'll need to change the default ctrl-up and ctrl-down key bindings in System Preferences to something else (in Keyboard -> Keyboard Shortcuts -> Mission Control : Mission Control and Show Desktop). On the first run it it will cycle through your desktops to enumerate them when you first change space. Then you should be able to change between desktops like in a 3x3 grid. There may be a few wrinkles, but it's basically functional, at least for me.
http://switchstep.com/ReSpaceApp This works, is free (right now) and is awesome. Just be sure to manually create as many spaces as your layout (in preferences) is expecting.
I'm on Mountain Lion and this seems to work for me. defaults read com.apple.spaces Look for "Current Space". You'll notice that running this command with different active spaces doesn't change the current space BUT if you check and uncheck a checkbox button in "System Preferences" and run it again, you'll see it updated. Hopefully this helps someone else! EDIT: It's ugly, but I'm using this: killall Dock && sleep 0.2 && defaults read com.apple.spaces | grep -A1 "Current Space" | tail -1 | awk '{print $NF }' | cut -f1 -d';'
on openNewSpace() tell application "System Events" —start mission control do shell script "/Applications/Mission\\ Control.app/Contents/MacOS/Mission\\ Control" tell process "Dock" set countSpaces to count buttons of list 1 of group 1 --new space click button 1 of group 1 --switch to new space repeat until (count buttons of list 1 of group 1) = (countSpaces + 1) end repeat click button (countSpaces + 1) of list 1 of group 1 end tell end tell end openNewSpace
I have come up with a workaround for this for myself in macOS Catalina, though I expect this should work for multiple macOS versions. This solution solves my problems, namely: The inability to identify which desktop contains which project, because desktops cannot be named. (I usually am splitting time on work on multiple projects at once and each desktop is dedicated to work on a different project [and they all use the same apps]) The inability to programmatically(/easily) determine which desktop I'm on at any one time The lack of tools to track time spent on each desktop I solved item 1 quite some time back using Stickies.app. I put the project name in a huge enough font that it's easily legible in the desktop thumbnails in Mission Control and I hide the stickie window behind my Dock, assigned specifically to the corresponding project's desktop. (I also duplicate the desktop name in small superscripted text that pokes out from under the left side of the dock so that I can identify the current desktop outside of mission control.) I just solved item 2 via applescript just now. In the stickie, I add a tiny, unobtrustive font string that identifies the stickie as the desktop name, e.g. 'dtop'. E.g. "small_superscripted_name LARGE_NAME tiny_dtop_string" or "project1 PROJECT1 dtop". Note, this script assumes that the project name contains no spaces (i.e. it's just one word). You can split on a different charcter/string, if you wish. Here is the applescript that, when run, results in the desktop name: tell application "System Events" --obtain the stickie with the desktop name set dstr to name of first item of (windows of application process "Stickies" of application "System Events" whose name contains "dtop") --Parse the desktop name from the stickie set astid to AppleScript's text item delimiters set AppleScript's text item delimiters to " " set dname to first item of (dstr's text items) set AppleScript's text item delimiters to astid --Show the result in a dialog window display dialog "Desktop: " & dname end tell And as far as item 3 goes, I have yet to implement it, but I could easily poll via a cron job by calling the script using osascript. However, I may explore the possibility of mapping the desktop keyboard shortcuts I use to trigger the script, say, after a delay like 1 second after a control-right/left-arrow, 10 seconds after F3 or control-up-arrow. (It wouldn't catch window-drags that trigger desktop changes, but that hasn't worked anyway since I started using 2 monitors.) Once I have that set up, I'll likely output the desktop name and a timestamp to a log so I can track time spent on each desktop. UPDATE: I did eventually solve item 3 with an Applescript run once a minute in a cron job. I also wrote a perl script to generate a bar plot of both: how much time spent on each project (i.e. desktop) over a period of time (e.g. the past week), and a per day plot showing how much time I spent on what projects each day. Here's an example: