Additional AppleScript Tools and Resources

 < Day Day Up > 

Understanding AppleScript Syntax

Describing the AppleScript syntax to a programmer familiar with a traditional language isn't as straightforward as you might think. AppleScript uses an entirely different programming model based on an English-like structure that, after a few minutes of use, leaves the programmer feeling as though he is having a deep, intellectual conversation with his computer.

TIP

Many of the building blocks discussed here can automatically be entered into the Script Editor by Control-Clicking within your script and then choosing from the many prewritten code fragments displayed in the pop-up menu that will appear. You'll still have to fill in the details, but you won't have to remember the exact syntax.


Sending Instructions to an Application: tell

The basic building block of an AppleScript is the tell statement. tell is used to address an object and give it instructions to perform. A tell line is written in one of two common forms: a block or a single statement. The block format enables the programmer to send multiple commands to an application without stating its name each time.

Single:

 tell <object> <object name> to <action> 

Block:

 tell <object> <object name>     <action>     <action>     <action>     ... end tell 

For example, the following two statements are identical but are structured using the simple and block forms of tell:

 tell application "Finder" to empty trash 

and

 tell application "Finder"     empty trash end tell 

Both of these short scripts cause Finder to empty the trash. Although the second form might seem more verbose, it is likely to be the most commonly encountered form. Most scripts interact with objects to perform complex compound operations rather than simple commands. In addition, the second version of the AppleScript is easier to read and view the functional components. Maintaining readable code is a good idea no matter what programming platform you're using.

TIP

In addition to breaking up code with tell blocks, long lines are typically split using a code-continuation character. To break a single long code line across multiple lines, press Option-Return to insert a code-continuation character.


Manipulating Variables: set/get

In AppleScript, variables are automatically created when they are set. A variable name can be any combination of alphanumerics as long as the first character is a letter. No special prefixes are required to denote a variable within the code.

Although type conversions happen automatically in many cases, a variable type can be explicitly given directly in the set statement:

 set <variable/property> to <value> [as <object type>] 

For example, both of the following lines set variables (thevalue and thevalue2) to 5, but the second line forces the variable to be a string:

 set thevalue to 5 set thevalue2 to 5 as string 

In addition to setting variables, the set command can act on the properties of an object to effect changes on the system. Earlier you saw how an AppleScript could get the title of the active Person object in Address Book. Similarly, set can alter the file type. For example:

 1: tell application "Address Book" 2:   display dialog "Enter a new job title for yourself:" default answer "" 2:   set myNewTitle to the text returned of the result 3:   set the job title of my card to myNewTitle 4: end tell 

In line 3 of this code fragment, set is used to alter the job title for the active user's card. Previously, we had only read information now we can change it!

As you've already seen, to retrieve values from variables or properties from objects, you would use the get command. get, by itself, retrieves the value of an object or variable and stores it in the result variable:

 get the <property/variable> [of <object>] 

Traditional programmers might feel uncomfortable with retrieving results into a temporary variable (result); in that case, they can use an implicit get to immediately store the results in another variable or object property:

 set <variable/property> [of <object>] to the <property/variable> [of <object>] 

Here the get is implied. This form of get and set is preferred for creating concise and readable code.

Working with Lists

You've seen that variables can take on simple values, such as numbers or strings, but they can also contain more complex values in the form of lists. Lists are equivalent to arrays in more traditional programming languages. A list is represented by a comma-separated group of values, enclosed in curly brackets { } . For example, the following line sets a variable, thePosition, to a list containing two values:

 set thePosition to {50, 75} 

Lists are often used to set coordinate pairs for manipulating onscreen objects but can contain any object. In fact, lists can even contain lists of lists. For example:

 set theListOfPositions to {{50, 75}, {65, 45}, {25, 90}} 

Here, a variable called theListOfPositions is set to a list of lists. Item 1 of the list is {50,75}, item 2 is {65,45}, and so on.

When dealing with list values, you can reference individual items within a list by referring to them as just that: items. For example, assume that you've run the following command:

 set thePosition to {50, 75} 

To retrieve the value of the first item in the list, use

 get item 1 of thePosition 

When dealing with lists within lists, just embed item statements within one another. Assume, for example, that this list has been entered:

 set theListOfPositions to {{50, 75}, {65, 45}, {25, 90}} 

To retrieve the value of the second item of the second list within a list, you could write

 get item 2 of item 2 of theListOfPositions 

List Abstraction

In many cases, the names of lists and the elements they contain have been abstracted within the application dictionaries. For example, the Address Book application defines People as the plural form of the Person object. In other words, People is a list of Person elements. Because we know this, we can access any person in the address book by referencing their item number in the people list.

For example, to reference the name of the first person in the people list, I could use:

 get the name of item 1 of people 

However, because the system already knows that a Person object is an element of the people list, this can also be rewritten as simply:

 get the name of person 1 

You can apply this same syntax wherever the dictionary includes an abstraction for a list and its elements.

Again, the power of these commands is based in the dictionaries of AppleScript applications. Exploring the scripting dictionaries is the best way to uncover the capabilities of the AppleScript platform.

Using Flow Control: If

A common programming construct is the If-then-else statement. This is used to check the value of an item and then react appropriately. The syntax for a basic If statement is

 If <condition> then     <action> end if 

For example, the following code asks the user to input a value, checks to see whether it equals 5, and outputs an appropriate message if it does:

 1: display dialog "Enter a number:" default answer "" 2: set theValue to (text returned of the result) as integer 3: if theValue = 5 then 4:   display dialog "Five is my magic number." 5: end if 

Line 1 displays a dialog prompt for a user to enter a value. Line 2 sets a variable theValue to the text returned from the dialog and forces it to be evaluated as an integer. Line 3 checks theValue; if it is equal to the number 5, line 4 is executed. Line 4 displays an onscreen message, and line 5 ends the If statement.

The If statement can be expanded to include an else clause that is executed if the original condition is not met:

 1: display dialog "Enter a number:" default answer "" 2: set theValue to (text returned of the result) as integer 3: if theValue = 5 then 4:   display dialog "Five is my magic number." 5: else 6:   display dialog "That is NOT my magic number." 7: end if 

In this modified version of the code, line 6 contains an alternative message that will be displayed if the condition in line 3 is not met.

Finally, the else itself can be expanded to check alternative conditions using else if. This enables multiple possibilities to be evaluated within a single statement:

 1: display dialog "Enter a number:" default answer "" 2: set theValue to (text returned of the result) as integer 3: if theValue = 5 then 4:   display dialog "Five is my magic number." 5: else if theValue = 3 then 6:   display dialog "Three is a decent number too." 7: else 8:   display dialog "I don't like that number." 9: end if 

The latest version of the code includes an else if in line 5. If the initial comparison in line 3 fails, line 5 is evaluated. Finally, if line 5 fails, the else in line 8 is executed.

Creating Iteration with repeat

Another common programming construct is the loop. AppleScript uses a single-loop type to handle a variety of looping needs. The repeat statement has several different forms that cover while, until, and other types of traditional loops.

There are six different forms of the repeat statement:

  • Repeat indefinitely Repeat a group of statements indefinitely or until the exit command is called.

     repeat   <statements> end repeat 

  • Repeat # Using the second loop format, the user can choose the number of times a loop repeats.

     repeat <integer> times   <statements> end repeat 

  • Repeat while Loop indefinitely while the given condition evaluates to true.

     repeat while <condition>   <statements> end repeat 

  • Repeat until Loop indefinitely until the given condition evaluates to true. This is the inverse of the repeat while loop.

     repeat until <condition>   <statements> end repeat 

  • Repeat with Called a for/next loop in more traditional languages, this form of the repeat loop counts up or down from a starting number to an ending number. Each iteration updates a variable with the latest loop value.

     repeat with <variable> from <starting integer> to  <ending integer> [by <increment>]   <statements> end repeat 

  • Repeat with list Like the standard repeat with style loop, the repeat with list loop runs over a range of values, storing each value in a named variable during the iterations of the loop. The difference is that the value range is specified with a list, rather than an upper and lower integer value. This enables the loop to operate over anything from numbers to strings to lists of lists.

     repeat with <variable> in <list>   <statements> end repeat 

For example, let's consider a short script that cycles through each of the individuals in your address book and displays the name of each. To do this, we'll need use the second-to-last loop type, and we'll also need to know how many people are in the address book and how to reference each one.

A bit of poking around in the dictionary quickly tells us that we can return the number of elements in any list by using the Standard Suite verb count with the syntax count of <list name>. We also learned earlier (in the section "List Abstraction") that Address Book abstracts the list of all Person objects as People, and that we can reference an individual element of the people list as simply person <#> so our script can be written as

 1: tell application "Address Book" 2:     repeat with i from 1 to count of people 3:          get the name of person i 4:          display dialog the result 5:     end repeat 6: end tell 

Creating Subroutines

An important building block that you'll need for creating large AppleScripts is the subroutine. Subroutines help modularize code by breaking it into smaller, more manageable segments that can return specific results to a controlling piece of code. There are two types of subroutines in AppleScript: those with labeled parameters and those that use positional parameters. A parameter is a piece of information passed to a subroutine when it is called.

Positional parameters will be the most familiar to anyone who has used another programming language. This type of subroutine, which is the easiest to define and use, depends on being called with a certain number of parameters in a certain order.

Labeled parameters, on the other hand, rely on a set of named parameters and their values, which can be sent to the subroutine in any order. This can be used to create an English-like syntax but adds a level of complexity when reading the code.

Because positional parameters can be used for almost any type of development and fit in with the structure of other languages discussed in this book, they will be the focus here.

The syntax of a positional parameter subroutine is shown here:

 on <subroutine name> ([<variable 1>,<variable 2>,<variable n>,...])   <statements>   [return <result value>] end <subroutine name> 

Each positional parameter-based subroutine requires a name, a list of variables that will be supplied when called, and an optional value that will be returned to the main application. For example, the following beAnnoying routine takes a string and a number as parameters, and then displays a dialog box with the message. The display will be repeated until it matches the number given.

 1: on beAnnoying(theMessage, howAnnoying) 2:   repeat howAnnoying times 3:     display dialog theMessage 4:   end repeat 5: end beAnnoying 

Line 1 declares the subroutine beAnnoying and its two parameters: theMessage and howAnnoying. Line 2 starts a loop that repeats for the number of times set in the howAnnoying variable. Line 3 displays a dialog box with the contents theMessage. Line 4 ends the loop, and line 5 ends the subroutine.

As expected, running this piece of code does absolutely nothing. It is a subroutine, and, as such, requires that another piece of code call it. To call this particular routine, you could use a line such as

 beAnnoying("Am I annoying yet?",3) 

This causes the subroutine to activate and display the message Am I annoying yet? tHRee times.

A more useful subroutine is one that performs a calculation and returns a result. The following example accepts, as input, an integer containing a person's age in years. It returns a result containing the given age in days.

 1: on yearsToDays(theYears) 2:   return theYears * 365 3: end yearsToDays 

Because this subroutine returns a value, it can be called from within a set statement to store the result directly into a variable:

 set dayAge to yearsToDays(90) 

When working in subroutines, you must explicitly define variables that are used only in the subroutine, as opposed to those that can be accessed from anywhere in the AppleScript application. A variable that is visible to all portions of a script is called a global variable and is defined using the global declaration. Similarly, the local keyword can be used to limit the scope of a variable to only the code contained within a subroutine. For example, try executing the following AppleScript:

 1: set theValue to 10 2: reset() 3: display dialog theValue 4: 5: on reset() 6:   local theValue 7:   set theValue to 0 8: end reset 

In line 1, a variable called theValue is set to 10. In line 2, the reset subroutine is called, which appears to set the contents of theValue to zero. Yet, when the result is displayed in line 3, the original value remains. The reason for this strange behavior is the inclusion of line 6. Line 6 defines theValue as a local variable to the reset subroutine. This means that any changes to that variable will not extend outside the subroutine.

To gain the behavior we expect (the contents of theValue are set to zero everywhere), swap the local keyword with global:

 1: set theValue to 10 2: reset() 3: display dialog theValue 4: 5: on reset() 6:   global theValue 7:   set theValue to 0 8: end reset 

This tiny modification tells the reset subroutine that it should use the global representation of the variable theValue. When theValue is set to zero in line 7, it replaces the initial value set in line 1.

Accessing the Command Line from AppleScript

AppleScript can easily be integrated with shell scripts using the do shell script command, which is part of the AppleScript Standard Additions dictionary. This function returns the results of the command in a variable that you can use in your scripts. For example, to return and display the output of the Unix command date

 do shell script "date"  display dialog the result 

As you work with the command line (see Chapter 9, "Accessing the BSD Subsystem," to get started), you'll learn that sometimes it's useful to execute commands as an administrative user (root). There are several additional parameters that you can use with do shell script to accomplish this and more. The full syntax follows:

 do shell script <shell commands>    [password <admin password> [with administrator privileges]] 

If the with administrator privileges clause is specified, the user is prompted for a password and the script executed with administrative permissions. To eliminate the password prompt, simply use the password keyword followed by your administrator password. For example, to return the contents of /var/log/secure.log (which requires administrator access), you could use

 do shell script "cat /var/log/secure.log" password "mypassword"   with administrator privileges 

Combining AppleScript with the power of the command line gives advanced users the ability to control all components of their system.

TIP

Users who just want to activate a shell script can do so by scripting the terminal. This method does not provide a means of returning shell results to the AppleScript.


An example of a Terminal script that uses the ls command to display a list of files is shown here:

 tell application "Terminal"   do script "ls" end tell 

Scripting the Unscriptable

Unfortunately for us, not all applications are directly scriptable. If you attempt to display an application's scripting dictionary and discover that none is available, you'll need to take a slightly different approach to automating its actions you'll need to use GUI scripting.

First introduced in Panther, GUI scripting allows you to control interface elements directly. You must, in script form, control a virtual user that clicks and types in all the right places. This isn't difficult, but it does require some additional software and the activation of GUI Scripting on your system.

To turn on GUI scripting, open the AppleScript Utility (path: /Applications/AppleScript/AppleScript Utility). Click the Enable GUI Scripting checkbox, as shown in Figure 4.18.

Figure 4.18. GUI scripting must be enabled before it can be used.


After GUI Scripting has been turned on, it's ready to use but because you must explicitly address each user interface element within an application, you'll need some additional software to identify how these elements are referenced. For example, a GUI script may invoke an action that looks like this:

 click radio button 3 

But because the name radio button 3 is determined by the structure of the application (and not necessarily its visual placement), you can't just guess and expect it to work.

To correctly identify GUI elements, you can either download Apple's UI Element Inspector from http://www.apple.com/applescript/uiscripting/02.html, or use a much (much) simpler tool PreFab Software's PreFab UI Browser found at http://www.prefab.com/

Priced at $55, PreFab UI Browser has a 30-day free trial and is much simpler to use than the Apple tool.

As a simple example, let's look at how you might retrieve the version and build number of the Camino browser within a script. This excellent browser (visit http://caminobrowser.org) isn't yet scriptable, so to retrieve the version, we have to work with directly with GUI scripting and the Camino interface.

First, start Camino and PreFab UI Browser, and choose Camino from the Target pop-up list this identifies that we will be working with the Camino application. Now, generate your first GUI scripting code by choosing Tell Block Wrapper (Short) from the AppleScript pop-up menu on the right side of the UI browser window. A new window will appear with the GUI scripting code skeleton for Camino, as shown in Figure 4.19.

Figure 4.19. Start by generating the code block that will work with the application you're trying to script.


Next, determine the path you would normally navigate through to get the information you want. For retrieving the browser version this involves opening the About Camino window and reading the contents of one of the window's text strings.

We start by simulating the mouse click on the About Camino menu item. Using the columns at the top of the PreFab UI Browser, navigate through the selections you would make in the GUI specifically, "menu bar 1", "menu bar item, " Camino", "menu 1", and, finally "menu item 'About Camino'", as demonstrated in Figure 4.20.

Figure 4.20. Navigate to the element you want to operate on.


After you've reached the element that you want to act on, use the AppleScript menu to choose an action. In this case, we want the Click Selected Element action. Choosing this action will generate a line of AppleScript that you can include in the tell block that was generated in the first step. The AppleScript should now resemble this:

 activate application "Camino" tell application "System Events"    tell process "Camino"      -- GUI Scripting statements:    click menu item "About Camino" of menu 1 of menu bar item "Camino" of menu bar 1    end tell end tell 

Now, if you haven't already, you'll need to either execute this script or manually open the About Camino window because the last step is to identify and retrieve the version string from the window.

After you've opened the About Camino window, click refresh in the UI Browser. You should now see an entry for "window 1" in the second column. Now would be a good time to click the Highlight check box in the browser. Doing so will automatically highlight the selected GUI element with a transparent color overlay. After you've turned on this option, click the "dialog 1" element and you should see the "About Camino" window highlight.

The next step is to identify which of the elements under "dialog 1" is the version number. Click through the various text items until the version number highlights, as shown in Figure 4.21. In the current release of Camino, it is "text 3".

Figure 4.21. Identify the text string that contains the version number by selecting each available element then viewing the highlight within the GUI.


Now we've identified the object we're interested in, so choose Reference to Selected Object in the AppleScript menu to generate AppleScript that will identify that object. UI Browser will return static text "Version 2005033112 (v0.8.3)" of window 1. Unfortunately, although this is accurate (you can reference the object based on its value), it doesn't do us much good if the value changes. Thankfully, we also know that the element can be called "text 3" from the UI Browser list of elements. This means our reference can simply become static text 3 of dialog 1.

We're not quite done yet, but we're really close. The final step is to retrieve the actual value of the element static text 3 of dialog 1. This GUI element contains additional attributes such as size, position, and so forth, so we can't simple reference it by its object name. We must identify which of the attributes is the actual text we need. To do this, choose Attributes from the Drawer menu at the bottom of the UI Browser. A list of all the attributes for the selected item is shown, as displayed in Figure 4.22.

Figure 4.22. Identify the attribute of the element that we're really looking for.


Scroll through the attributes until you see the version string. It is stored simply as the value attribute of the element. Finally, we can now reference the version string as the value of static text 3 of dialog 1!

A complete script that retrieves this value into an AppleScript variable and then displays it is as simple as

 activate application "Camino" tell application "System Events"    tell process "Camino"      -- GUI Scripting statements:    click menu item "About Camino" of menu 1 of menu bar item "Camino" of menu bar 1      set CaminoVersion to the value of static text 3 of dialog 1    end tell end tell display dialog CaminoVersion 

Using GUI scripting you can get, set, and use all the other AppleScript features on a normally nonscriptable application. Obviously you will need to spend some time with PreFab's UI Browser to get the job done, but it's better than no scripting at all.

     < Day Day Up > 


    Mac OS X Tiger Unleashed
    Mac OS X Tiger Unleashed
    ISBN: 0672327465
    EAN: 2147483647
    Year: 2005
    Pages: 251

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net