AppleScript for Beginners VI - User Interaction

Before we explore all the fascinating ways for a script to communicate with the outside world, I should mention that none of what we will deal with today is exactly AppleScript. Rather, these commands (as well as a host of others that greatly enhance the AppleScript experience) are part of a Scripting Addition called StandardAdditions that comes, well, standard with AppleScript. The common abbreviation for a scripting addition is OSAX, which sort of stands for Open Scripting Architecture Extension. Anybody who understands Objective-C and/or Cocoa can write an OSAX that would further the usefulness of AppleScript, and many people have done just that. There are a large number of these additions available out there, all designed to increase the functionality and simplicity of your scripts. You can find a lot of them right here at MacScripter. I strongly recommend that you open the StandardAdditions library and see all the nifty cool commands in all the different categories. We will be working in the User Interaction section today; the first on the list.

Now, let’s return to the parting script from the last session:


tell application "iTunes"
	set all_playlist_names to the name of every playlist
	set your_choice to item 1 of (choose from list all_playlist_names with prompt "Pick a playlist, any playlist...")
	set the_guess to text returned of (display dialog "Guess how many tracks are in playlist " & your_choice default answer "")
	if (the_guess as number) = (count (every track of playlist your_choice)) then
		display dialog "WOW! You are a genius! Enjoy this track." giving up after 4
		play track (random number from 1 to (count (every track of playlist your_choice))) of playlist your_choice
	else
		display dialog "Wrong!!" & return & "There were " & (count (every track of playlist your_choice)) & " tracks. You were off by " & ((count (every track of playlist your_choice)) - the_guess)
	end if
end tell

If you have tinkered with this script, you know it is nothing more than a silly guessing game for how many tracks are in your different iTunes playlists. Yes, it appears very busy, and contains a lot of parentheses. Let’s talk about those first, since that is why there are so many.

AppleScript treats parentheses just like you would if you were performing maths; the commands within the parentheses are dealt with first, starting from the innermost set to the outermost set of parentheses. Personally, I tend to use a lot of parentheses in my scripts, and I know that about half of them are unnecessary, but I like them, and it helps me keep things straight as I go along, or if I have to go back some time and edit previous work.

OK, let’s work through this script, starting inside the tell block. The first line is simple, we create a list of all the names of the albums in our iTunes library and set it to a variable entitled all_playlist_names. When the very next line is executed, up pops up a nifty looking box on the screen (click on the image to see it full size):

Screenshot of Choose From List Text Box
This is an example of the choose from list command, which is very useful, but slightly quirky in how it works within the script. Take a closer look at the line that produces this box:

set your_choice to item 1 of (choose from list all_playlist_names with prompt "Pick a playlist, any playlist...")

We are setting the variable your_choice to item 1 of the stuff inside the parentheses (which we will address in a moment). The stuff in the parentheses, as you will see shortly, is whatever the user chooses from that box that pops up when the script executes. The reason we must state it as item 1 is because AppleScript returns a list to the script, even if only one item is chosen. For example, if you had a list of numbers {1,2,3,4}, and you prompted the user to choose one (let’s say 3), AppleScript would return that 3 in the form of a single item in a list: {3}, not just the three. Of course, you can certainly set the variable your_choice to the stuff in the parentheses, the script is fine with that as well, but YOU as the scripter must remember that it is in the form of a list, not a single object. In this example, I have elected to set the variable to item 1 of the stuff in the parentheses, since I know it is going to return a list of one item, and it is that one item that I want to use later. (There is actually a very good reason for the choose from list command to return a list. If you look it up in the StandardAdditions dictionary, you will see that one of the options when using this command is multiple selections allowed. If you were to set this up so that a user could select multiple items, it would needs be a list that you work with after the selections are made.)

OK, let’s move to the stuff within the parentheses: (choose from list all_playlist_names with prompt “Pick a playlist, any playlist…”) You should be able to read this and get an idea for what is happening. Since it is within parentheses, we know that the computer will process this first (even though I am explaining it second…). When you use the command choose from list, you next identify the variable (or the list itself) from which you would like someone to make the choice. In this case, we are using the list generated in the previous line, which is simply all the names of the playlists in the user’s iTunes library. That is the only requirement when using this command; the latter part with prompt is optional in case you want to say something specific to the user as part of the box that pops up. For instance, something as basic as this works as well:


choose from list {1, 2, 3, 4, 5, 6}

You see that the native prompt is a simple Please make your selection:. Nothing wrong with that, so go ahead and use it if you don’t care about writing your own prompts.
Once we put it all together, the variable your_choice becomes the choice that user makes from the list of all iTunes playlist names. By setting it to item 1 of the choose from list command, we end up with a single item that is no longer in list form. Good.

On to the next line:


set the_guess to text returned of (display dialog "Guess how many tracks are in playlist " & your_choice default answer "")

Here is where we get to discuss what is by far the most common method of user interaction, the versatile display dialog. There are nearly a dozen different parameters that you can use to modify this tremendous little tool, and I encourage you to spend some time in the StandardAdditions dictionary entry for display dialog and reading through the items. You really can do nearly everything (except make french fries) with this nifty command. In our example, all we want is a guess from the user of how many tracks are in the playlist she or he just chose. Once again, we are going to consider the stuff inside the parentheses last. You see that we are setting another variable, the_guess to text returned of the stuff inside the parentheses. That is just what it sounds like; whatever the user types in as a response to the stuff in the parentheses will become the value of our new variable. Simple, right

So what is going on inside those parentheses I have tried to make a simple, yet interesting example of how much latitude you have as the scripter in all these user interaction tools. Here is the code once more:


(display dialog "Guess how many tracks are in playlist " & your_choice default answer "")

The display dialog command will put up a nifty box with whatever text you like inside of it, and in the native form, two buttons as well: Cancel & OK. You can build a text string almost any way you wish, using variables, dates, list items (not a whole list), or anything else you can think of that can be coerced into a string. You use the ampersand (the & character) to connect (concatenate) all your items into the string you create. This text string that you create will appear magically inside the display box, just above the buttons. These same text string creation rules apply whenever you coommand a display dialog, or even other dialogs (like the choose from list we already discussed). In our example, we build a string by providing the first half ("Guess how many tracks are in playlist ") and then concatenating the content of the variable your_choice, which is the name of the playlist chosen by the user. Notice how the text in quotes has a space at the end, before the terminal quote. Remember that. Whenever you add the content of a variable to a string value, adding a space is nearly always necessary, unless your variable value also contains a space. Without the space, the last word of the quoted text would be right next to the value of the variable, and it would look too weird on the screen.

The final command inside of the parentheses is default answer, indicating to AppleScript that the answer block should contain whatever it finds in the quotes. In our case, the quotes are empty, so the dialog box shows an empty text region. You can just as easily use something like “Type answer here” or even a number. Whatever value you place within the quotes will be highlighted upon execution, so the user does not need to actively delete the content; she or he just needs to start typing. The default answer is REQUIRED if you actually want to capture something from the user. For instance, try this out:


set the_guess to text returned of (display dialog "Guess how many tracks are in playlist " )

You get the dialog box all right, but there is nowhere to type in a response, so when you click OK, an error results. Just remember that if you want something back, you have to provide a default answer, even if it is a blank.

OK, we now have three variables locked and loaded; all_playlist_names is a list of the names of all the playlists in the user’s versions of iTunes, your_choice is the user’s choice of one item from that list of playlist names, and the_guess is a number (hopefully) that the user has typed in as a guess for how many tracks are in the chosen playlist. (I should mention here that if the user types in a string, the whole script will fail. Give it a try, and then see if you can come up with a solution for that eventuality, like checking to see if the typed in information is a number or not.)

With these three variables, we are ready to run the test to see how our user did on the guess. As you remember from last time, we set up a little if/then/else block to run the test and then perform different actions based on the results. This line actually performs the test:


if (the_guess as number) = (count (every track of playlist your_choice)) then

We take the variable the_guess, coerce it to a number (it may not be necessary, but it is a good idea nonetheless), and compare it to the number of items in the chosen playlist, using the count function on the playlist that resides in the variable your_choice. And yes, we could just as easily had used the variable all_playlist_names to perform the count, the number would be the same. In this case, it is only a matter of preference. Again, pay attention to the parentheses; as I stated earlier, I tend to use more than are necessary, but only because it makes things clearer (to me, at least) for reading and understanding what is happening. In this case, if both numbers are the same, the script accesses this line:

display dialog "WOW! You are a genius! Enjoy this track." giving up after 4

A nice WOW dialog congratulates the good guessing user, and the box goes away all by itself after 4 seconds, unless the user clicks the OK button first. That is what the giving up after 4 command does. It simply waits 4 seconds for user input, then it gives up and closes the box down all by itself, and moves along to the next line in the script. You see that on the next line, the script instructs iTunes to play a track. There are a lot of parentheses here as well:

play track (random number from 1 to (count (every track of playlist your_choice))) of playlist your_choice

The innermost set again counts the number of tracks in the chosen playlist. The outermost parentheses use a nifty command random number to pick a random number from 1 to whatever the total number of tracks is in that playlist. That entire group of scary looking commands will reduce down to a single number, and it is that numbered track of the user chosen playlist that iTunes is directed to play. Don’t let this confuse you or frighten you. In fact, let me show you how to do this kind of reduction. Let’s start off with a script that does the same thing, and reduce it line by line:


tell application "iTunes"
	set all_playlist_names to the name of every playlist
	set your_choice to item 1 of (choose from list all_playlist_names with prompt "Pick a playlist, any playlist...")
	set a to (count (every track of playlist your_choice))
	set b to (random number from 1 to a)
	play track b of playlist your_choice
end tell

We preserved the first two lines of our original script, just to get a user chosen playlist. The last three lines are where we want to focus. We use a to represent the number of tracks in the chosen playlist, and we use b to represent a random number from 1 to a. We then simply tell iTunes to play track number b of the chosen playlist. To reduce it by one line, we simply take the thing we set to a, and put it in the place of a in the next line, like this:


tell application "iTunes"
	set all_playlist_names to the name of every playlist
	set your_choice to item 1 of (choose from list all_playlist_names with prompt "Pick a playlist, any playlist...")
	--set a to (count (every track of playlist your_choice)) --We just take this code, and replace the a that was in the next line down.
	set b to (random number from 1 to (count (every track of playlist your_choice)))
	play track b of playlist your_choice
end tell

And now, we do the same for the b variable:


tell application "iTunes"
	set all_playlist_names to the name of every playlist
	set your_choice to item 1 of (choose from list all_playlist_names with prompt "Pick a playlist, any playlist...")
	--set b to (random number from 1 to (count (every track of playlist your_choice))) --We just take this code, and replace the b that was in the next line down.
	play track (random number from 1 to (count (every track of playlist your_choice))) of playlist your_choice
end tell

And voila! We end up with a nice, neat one-line bit of AppleScript, and we eliminated two variables at the same time! You will find this sort of progression a lot of fun if you give it a chance. Try writing your code out one line and one variable at a time, then start reducing lines one variable at a time and see how far you get. There are some applications that won’t tolerate certain concatenations, but if you test each iteration as you go along, you will find those spots easily.

Now, let’s briefly return to our original script. If the user does not correctly guess the number of tracks in the chosen playlist, the else portion of the if/then/else block comes into play, and this line is executed:


display dialog "Wrong!!" & return & "There were " & (count (every track of playlist your_choice)) & " tracks. You were off by " & ((count (every track of playlist your_choice)) - the_guess)

This dialog contains a lot of information, and a lot of parentheses to make it work. You need to take the time to understand how the string was put together, so I will leave it to you to discover the details, but in essence, it displays how many tracks were in the playlist, and the calculates how far off the guess was. One thing I will point out is the term return that is right after the Wrong!! When you are building a string, you can use the terms space, tab, or return to add spaces, tabs, or returns to the text strings you wish to display in your dialogs.

Play around with them and see what you discover. One very useful purpose for dialog boxes is to help de-bug your scripts and catch errors. When your scripts get longer, and you find yourself getting lost therein, it can be tough to find the dysfunctional portions. In most cases, you just need to know the value of a variable at different places in the script, and a well placed display dialog can really help with that. In fact, the script I leave you with (see below) this time introduces that topic. HINT: You only need to add a single term somewhere for this script to function properly.

This discussion only scratches the surface of what you can write to interact with the user(s) of your scripts. The StandardAdditions OSAX contains dozens of useful features, and your scripts can only become more powerful and versatile as you learn to use them. Until next time,


tell application "iTunes"
	set five_tracks to {}
	repeat 5 times
		try
			set end of five_tracks to some track of library playlist
		on error errTxt number errNum
			display dialog errTxt & return & errNum
		end try
	end repeat
	try
		play some item of five_tracks
	on error errTxt number errNum
		display dialog errTxt & return & errNum
	end try
end tell