상세 컨텐츠

본문 제목

Create(scripting.dictionary Exists For Mac

카테고리 없음

by canmorire1985 2020. 1. 25. 10:56

본문

Create(scripting.dictionary Exists For Mac
  1. Create(scripting.dictionary Exists For Mac Free
  2. Scripting Dictionary Object

Last month I wrote an article for MacStories on the. The second half was a basic overview of JavaScript for Automation (JXA) (the new addition to OS X scripting languages) joining AppleScript. Before writing that section of the article, I wanted to learn the basics of JXA in order to be sure that I understood what I was writing about and wasn't just blindly summarizing the contents of the. Since JXA is so new, there obviously was not much information to go. I've never gotten around to learning AppleScript, so articles based on the classic OS X automation language were not much help either, although I’m not sure how much they would apply anyway. The result was quite a few wasted hours trying to figure out some of the most basic parts of JXA, such as proper syntax of method calls, which method calls worked with which apps, and how to identify UI Elements in order to trigger them with UI automation.

Jun 13, 2016  Through scripting, you can create powerful workflow solutions that enhance productivity, reduce errors, save time, and save money. There are many different scripting languages. On the Mac, the primary ones used for automation are AppleScript and JavaScript. Mac: How to Create a Basic Script With Automator By Trevor Dobrygoski – Posted on Sep 3, 2009 Dec 17, 2009 in Mac If you are one of the growing number of Apple users, you have likely come across a strange looking robot guy in your Applications folder.

Eventually I was able to figure these things out, so now I'm back to share what I learned. What Not to Expect from this Article This article is not a comprehensive tutorial on the JavaScript programming language.

If you read any further, I am assuming you already have at least a basic knowledge of JavaScript in its web implementation, and simply want to transition from scripting websites to scripting Mac apps. If you do know JavaScript, this article is not meant to teach you every aspect of JXA either. I am not going to get into the JXA Objective-C bridge, as I do not know Objective-C, so that most powerful section of JXA is beyond my scope. Rather, the purpose of this writing is simply to give you the tools to get started with JXA and enable you to do some basic automating of applications using supported method calls or UI automation. Getting Starting with JXA The first thing to know is that JavaScript for Automation scripts are meant to be written in Apple's Script Editor app. Script Editor comes preinstalled on every Mac: you can find it in the Utilities folder in your Applications folder.

Script Editor is not the best code editor. I'm disappointed by its half-baked syntax highlighting and complete lack of code completion, but the convenience of being able to run JXA scripts at any time within Script Editor by clicking the play button, see the output in the bottom pane, and have easy access to the scripting libraries of all the apps on your machine outweigh the app's drawbacks. You can write your JXA in whatever editor you want, but I'll be writing about doing so in Script Editor.

When you open Script Editor you will see two main sections, a large one on top and a smaller one beneath it. The big section is where you will write your code, the small section will display data about your script while it is being run. This small section defaults to the Description view (which switches to the Result view when you run your script), but I have found the Events view to be far more useful, as it outputs every event that happens while your script is running rather than just the end results. You can switch to the Events view by clicking the third button at the very bottom of Script Editor (the box with lines in it) and then selecting the Events tab. Above the code section are four buttons. You only need to worry about the last three for most basic scripts. The button with the square in it will stop your script while it is still being executed (most useful for killing infinite loops), the triangle button will build and run your script, and the hammer button will just build your script (use this to make sure you don't have any syntax errors without actually running the code if you do not, or to get Script Editor's non-live syntax highlighting to catch up with any new code since your last build or run).

Below the four buttons is a thin row with a dropdown menu that will likely be set to “AppleScript”. Change this to “JavaScript”. (You can tell Script Editor to default to JavaScript on new script files by going to Preferences and changing the default to “JavaScript (1.0)”). With Script Editor set up, all that's left is to decide what apps you want to automate and then write your scripts.

There are two main ways to automate applications on OS X: using method calls built into apps by their developers, or UI automation. Of these, automating apps with their built in methods is significantly easier and less finicky. All you need to know is what methods a particular app supports, and this information is easily accessible in the Script Dictionary. The Script Dictionary is available via the menus of Script Editor either by going to Window Library, or Finder Open Dictionary. The latter shows you every available app that can be scripted, but then disappears when you open a particular app's dictionary. The Library option will show you a shorter list of scriptable apps to open dictionaries for, but will remain open for you to easily access multiple different dictionaries while writing a script.

You can add apps to the Library by clicking the plus button on its small window. Once you have an app in mind that you wish to automate, open its dictionary and you will get a list of all available methods and properties that the app includes and supports scripting of. For automation via method calls, these dictionaries are your friends.

Example Scripts The main reason I found it so difficult to get started with JXA myself was because there was a severe lack of sample code for me to compare to what I was reading in the release notes and script dictionaries. As such, for this article I built two scripts which we will follow along with to make my descriptions of JXA code more easily understandable.

The JXA release notes (linked at the beginning of this article) are a great tool for learning JXA, and you should definitely refer to them while you're getting started as well, but hopefully my scripts will give you some useful strategies and example code to refer to alongside the release notes to give you a more complete picture. For these scripts, I chose to automate iTunes, as it is an app that everyone will have installed which also supports a fairly extensive (although not all encompassing) scripting library. As we all know, iTunes 12 has a. As such, I wrote these scripts in an effort to reduce the number of times I have to deal with it on a daily basis. The most common thing I do with iTunes, not surprisingly, is play music from it. Personally, I keep a single main playlist of my 100 favorite songs (I update it often, moving old songs out and new favorites in, to keep things interesting) which I shuffle through.

The vast majority of times that I want to play music in iTunes, I just want to hit play on this playlist. Unfortunately, if the iTunes interface is not already open to this playlist, I have to click a series of buttons to navigate back to the correct section of the app, open the playlist, and choose the shuffle button.

Create(scripting.dictionary Exists For Mac

I wanted a script that would do this for me. The first script we'll look at here does just that. Given the title of a playlist that exists in your iTunes, the script will use iTunes' built in JXA methods to automatically play that playlist. The script does not specify shuffling, so it will play with whatever setting you had it set up for last. In other words, if you aren't a shuffler like I am, you can still make use of this script too, if it interests you.

When the script is run, the first thing it does is identify the current player state of iTunes, which will either be “stopped”, “playing”, “paused”, “fast forwarding”, or “rewinding”. I can't imagine why I would run the script when iTunes is in a state of fast forwarding or rewinding, so I do not account for these options.

Rather, if iTunes' state is playing, the script will throw up an alert which displays the title and artist of the current song along with the time remaining, and gives options to pause the song, restart the playlist (if you have shuffle turned on, this will reshuffle the entire playlist), or cancel and do nothing. If the state is paused, I get the same alert except the pause option is swapped for a play button and the message is tweaked to tell me the song is paused. If the state is stopped, the alert message tells me nothing is playing and gives only two options: play my playlist or cancel.

Now, let's jump in. Automation via Method Calls The first JXA method you need to know is the Application method. This method will return an application by name from your Applications folder, and once you have that application, you can call its own built-in methods. For us, we'll start with: iTunes = Application('iTunes') With this line, we can now call any method available in the iTunes script dictionary on our new iTunes object. The first thing our script needs to do is get the player state of iTunes so that it knows which alert to put up.

To access this, open the iTunes script dictionary in Script Editor (Window Library Double click iTunes in the list). The top section of the dictionary window is divided into three panes, the first of which will have a list of “suites”. Each suite contains a variety of properties and methods accessible from our iTunes object. The Standard Suite will be present in almost every app, and generally contains the same set of properties and a few methods which are not particularly useful (print settings for iTunes?). We want the iTunes Suite. Select that and the second pane will fill with objects and methods available specifically for the iTunes application.

Each list item has a small icon next to it. A “C” in a blue circle indicates a method you can call, a “C” in a purple square indicates an object. If you select an object, the third pane will fill with elements and properties of that object (yellow “E” icons are for elements and purple “P” icons are for properties). Beneath the three small panes at the top is a much larger section which displays the details for each object or method that you select.

Mac)

You can scroll through all of these to find what you're looking for, but it's much easier to identify it from the lists at the top, where clicking any item will jump you directly to the detail view in the bottom section. One of the most useful objects is the Application object, which you can find a version of for every application dictionary in the app-specific suite. If we take a look at the Application object in our iTunes Suite, we'll see this. Now you can start calling commands on the iTunes process to puzzle out its user interface. One important thing to note: UI Automation is only possible on apps that are open in the active desktop.

If you, like me, keep multiple desktops open with different apps on each one, make sure that your iTunes window exists on the desktop you are trying to automate its UI in. In this, case, make sure your iTunes window is open in the background of the desktop you are running Terminal commands in. To start figuring out the UI, it's useful to know that if you send your iTunes object a UIElement in the form of a command (add opening and closing parentheses after it), Terminal will return every instance of that UIElement that currently exists in your iTunes window. Since you are most likely trying to automate something within the main window of iTunes, start by calling this command: iTunes.windows Which will return this: = Application('System Events').applicationProcesses.byName('iTunes').windows.byName('iTunes') That return tells us a lot.

The first part, Application('System Events').applicationProcesses.byName('iTunes'), just defines what our iTunes object is: an ApplicationProcess named iTunes that is an element of the System Events application. After that, the information is much more useful:.windows.byName('iTunes') tells us that there is a single Window object, and its name is “iTunes”. Since there is only one window, and everything we want to automate is going to be within that window, we now have the second piece of our iTunes “file path”. Instead of just trying to find elements in iTunes, we will now look within iTunes.windows'iTunes'. The next piece is not so easy.

Create(scripting.dictionary Exists For Mac

It's obvious that we will be looking inside of the main iTunes window, but from there the UI could be organized in a nearly infinite number of ways. Thankfully, there is an incredibly useful command we can call to help us: the entireContents command. This command will return every UIElement within an app's user interface hierarchy. It basically reveals a map of the app's current window, which we can then follow to isolate the elements that we want to “click” on. If you put iTunes.windows'iTunes'.entireContents into your Terminal window, the result is an intimidating wall of text. Every element in this giant list is separated by a comma and a space, and I've found the list significantly easier to parse by copying it into a text editor and doing a Find-and-Replace to switch out every instance of, (a comma followed by a space) with two newlines.

That will separate every element in the iTunes interface onto its own line, with blank lines above and below it. This makes it far easier to find what you're looking for. As you scan through the list, you'll see increasingly large entries as you get deeper into the app's hierarchy. Here's a random entry from my list: Application('System Events').applicationProcesses.byName('iTunes').windows.byName('iTunes').radioGroups.at(0).radioButtons.at(2) This “file path” targets the RadioButton with an index of 2 within the RadioGroup with an index of 0 within the Window named “iTunes” within the iTunes ApplicationProcess of the System Events Application.

That's great, but it doesn't really help in determining what that button does. Thankfully, there's a command for that (multiple, actually).

Take a look back at the UIElement entry of the System Events script dictionary. Below the list of contained elements is a Properties list, and this includes every property that is attached to a UIElement. Whether or not all of these properties are defined is up to the app developer, and in the case of iTunes, if you ask for the name of most elements, the return will be null. Thankfully, they did define the description property for every UIElement that I have looked at in the app. So if we want to find out what the mysterious RadioButton at index 2 that we identified above is, we need to convert that entry from the list into dot notation, then just add.description to the end of it. Converting to dot notation is fairly easy. First, Application('System Events').applicationProcesses.byName('iTunes') refers to the iTunes system process in the System Events application.

We already created an object for that earlier, so that can be replaced by iTunesController. The rest is even easier: replace any instances of.byName or.at with hard brackets around whatever is between the parentheses. Thus,.windows.byName('iTunes') becomes.windows'iTunes',.radioGroups.at(0) becomes radioGroups0, etc. Here's the result: iTunesController.windows'iTunes'.radioGroups0.radioButtons2.description = 'TV Shows' Now take a look back at the iTunes interface.

Looking through the buttons that exist, we see that the third button in the row of buttons beneath the title bar is labeled “TV Shows”. That is the button that the path is targeting.

So if we wanted to automate navigating to the TV Shows section of the iTunes interface, we could just call the click command on that path, like so: iTunesController.windows'iTunes'.radioGroups0.radioButtons2.click Run that in your Terminal window and you will see your iTunes window switch over to the TV Shows section. And there you have it, the basics of finding your way through an application's user interface hierarchy.

An Example Script for UI Automation So to put this information to use, let's do a quick run through of the second script I prepared for this article – a UI automation script for iTunes. This script does something that iTunes, for some reason, does not seem capable of doing with its built-in methods: playing a station in iTunes Radio. Specifically, I want to automate the process of hitting play on my first custom iTunes Radio station. To begin, we'll set up an object for the iTunes system process, the same way that we did earlier: system = Application('System Events') iTunesController = system.processes'iTunes' With UI automation, particularly in an app like iTunes with so many different views, you can't assume which view you will be in at any given time. Thus, the first thing to do is make sure we are in the iTunes Radio view. However, if we are in, say, the TV Shows view at the moment, the iTunes Radio button actually does not exist.

This is UI automation, so we cannot call commands on any buttons that are not explicitly visible in the UI. This means that before we can get to iTunes Radio, we need to make sure we are in the main “Music” tab. Earlier, we already identified the TV Shows button, and the Music button is clearly in the same group of buttons in the interface. Since we know the TV Shows button had an index of 2, it would follow that the first button in that group would have an index of 0, and indeed, we can navigate iTunes to the Music tab using that path and the click command, like so: iTunesController.windows0.radioGroups0.radioButtons0.click Next, we need to to get to the iTunes Radio tab of the Music interface. Looking through the list of UIElements in the iTunes window, we're lucky in that iTunes makes it easy for us. Instead of only giving a button index for the iTunes Radio button, the entry actually displays it by name: Application('System Events').applicationProcesses.byName('iTunes').windows.byName('iTunes').radioGroups.at(1).radioButtons.byName('Radio') So next in our script, we'll convert to dot notation and then pass the click command to that element, navigating iTunes to the iTunes Radio tab. The result is as follows: iTunesController.windows'iTunes'.radioGroups1.radioButtons'Radio'.click The iTunes Radio interface is made up of a series of images, organized in rows.

The top row is for featured stations, the bottom row is for custom stations. I want to play my first custom station, so I need the index of the first item in the second row of images. If you were in a view in iTunes other than the iTunes Radio view when you created your entireContents list, your list will not include the elements in the iTunes Radio view, as they did not exist in the interface at the time. So if this is the case, call that command in your Terminal again, this time with the right view open, and now your list will include the elements that we are looking for, we just have to find them.

We know that the iTunes Radio station buttons are actually images, which means that we are looking for Image elements rather than Button elements. There will be two groups of Image objects, one for the row of featured stations and one for the row of custom stations. I only have two custom iTunes Radio stations, so I also know that my custom station list will only have two elements, an Image at index 0 and an Image at index 1. The featured station list will have five images in it. Looking through my list of UIElements, I do find two places where there are image elements, one is a list of five image elements and the other is a list of two. Calling the description command on the element at index 0 of the list of two images returns this: iTunes.windows'iTunes'.scrollAreas1.uiElements0.images0.description = 'Good Songs' The iTunes Radio station that I want to target is titled “Good Songs” (yes, the same as my playlist. I'm not very original, I just want to listen to good music), so I know that I'm in the right place.

Now I just add that to the script and give it a click command: iTunesController.windows'iTunes'.scrollAreas1.uiElements0.images0.click When you run this through your Terminal, your first iTunes Radio station will expand in your iTunes window in the background, the same thing it does if you actually click on it. Now we just need to target the small play button within the expanded station view and click it to begin playback. Don't waste your time looking through the previous list of uiElements to find this play button though, because it won't be there. When we first called the entireContents command on our iTunes window, the iTunes Radio station was collapsed, meaning that in the eyes of iTunes' accessibility framework, none of the elements within the expanded view existed. Now that the station is expanded, we can call iTunesController.windows'iTunes'.entireContents again, and the new list will include the play button we are looking for. The new list is even bigger than the last one, and since we are looking for a Button instead of an Image, it's harder to narrow down the choices. However, if we do a quick scroll through the list (after expanding it with a find/replace to make it easier to navigate), we'll see that iTunes had made it easy on us.

Create(scripting.dictionary Exists For Mac Free

Within the interface of the expanded station view are lists of songs, one for the history of songs you've listened to on the station, one for the songs you've flagged for the station to play more of, and one for the songs you've flagged to never be played. Within our list of elements, we'll see that iTunes kindly displays all of those song elements by name instead of by index.

In my particular interface, the top song on my “Play More Like This” list is “All Eyes on You” by St. A quick search for “all eyes” in my TextEdit window where I pasted my entireContents results jumps me right to the UIElement I'm looking for. The play button is just slightly above that song on the interface, so I can scroll up through my list of items and assume it is not too far above. A little way up my list, I see a Button element with the name “Good Songs”, the title Button for my station. Above this are two unidentified Button elements, which I can venture to guess correspond to the only two other buttons near the station's title: the play and share buttons. Calling description on the first of these buttons returns = 'play', and with that I've found what I'm looking for. Regardless of what your station looks like, the path to the play button will be the same, so the last line in our script, which will start playback on the first iTunes Radio station in your My Stations list, will be as follows: iTunesController.windows0.scrollAreas1.uiElements0.groups0.buttons0.click There's one last thing to do, and it's because of a drawback with UI automation.

If you watch the iTunes UI when you click on an iTunes Radio station, you will see that the station expands with an animation. What this means to our script is that there is a fraction of a second between clicking the iTunes Radio station Image and having the station fully expanded. During this small amount of time, the uiElements within the expanded view are still not available to the accessibility framework. While in the majority of my tests this did not cause issue, there were some times when the station would not expand fast enough, and the command that was supposed to click the play Button would end up clicking the Button at the index before the view expands.

Thankfully, there is an easy fix for this, a fix that should help if you find this happening in any of your scripts: the delay command. We can add one more line to our script and tell it to delay for half a second between clicking the station Image and clicking the play Button, so that we can be certain the animation has completed. You might have noticed that I used both “UIElement” and “uiElements”. As you look through the script dictionaries, you'll see discrepancies in capitalization between singular and plural elements a lot. This confused me at first, but eventually I realized that this is simply the way JXA syntax works. One UIElement, two uiElements. One ScrollArea, two scrollAreas.

Basically, singular objects capitalize the first letter of each word while plural objects are camel-cased. I don't know why this choice was made, but the important thing to note is that in nearly every instance within your scripts (at least in every instance that I have encountered) you will use the plural, camel-cased version when you are accessing objects. Within the script dictionaries though, objects are referred to in singular form, except in the Elements section of an object's description, where elements are pluralized and therefore camel-cased. Keeping with our file system analogy, Button objects can be thought of like files. When you get to a file in a file system, you know you're at the end of the line for that file path: you can't go any deeper with that file as part of your path.

You can copy a newline onto your clipboard in Terminal by clicking after the last character on one line and then dragging down to before the first character on the next line. Your selection should then appear to contain all the blank space after the last character on the first line. You can copy this and then paste it twice into the replace part of your text editor to swap comma-spaces for double newlines. This was tricky for me to wrap my head around at first. If you scroll the featured stations list, you will see that there are far more than five stations in it. However, since UI automation only sees elements actually on the screen at any given time, it can only see as many stations as fit on the screen at once. For me, on my 13 inch retina MacBook Pro, a max of five iTunes Radio stations fit on my screen at any given time.

Scripting Dictionary Object

If I scroll my list to the side, the station at the index of zero will no longer be the first station in the featured list, but instead will be whatever station is located in the far left part of the screen at the time I access the interface's list.

Create(scripting.dictionary Exists For Mac