The Monty Hall problem

The Monty Hall problem is based on a TV game show where you, as the contestant, are presented with three doors. Behind one door is an awesome prize. You just have to guess which door has the prize behind it.

After you make your choice (C), the host reveals what is behind one of the other doors (it’s never the awesome prize). Then you are given the option to stay with your original decision, or change to the other remaining door.

The problem is this: Should you change your mind and choose the other door, or should you stay with your original choice?

NOW, there’s a mathematical proof that you should definitely change doors. And I wanted to set up an Applescript to demonstrate that this is so.

You COULD create a script which let’s you emulate the game. The below script let’s you choose a door, and then change your mind (or not) and gives you a readout of your progress (wins vs losses).

And that can be a bit of fun … for about 2 minutes … tops.

set TheDoors to {"Door 1", "Door 2", "Door 3"}
set TheWin to 0
set TheLoss to 0

my mgPlay(TheDoors, TheWin, TheLoss)

on mgPlay(TheDoors, TheWin, TheLoss)
	set WinningDoor to some item in TheDoors
	set ChosenDoor to (choose from list TheDoors with title "Monty Hall" with prompt "Which door do you choose?")
	if result is false then
		error number -128
	end if
	set ClosedDoors to {WinningDoor, (item 1 of ChosenDoor)}
	set OpenDoors to {}
	repeat with ThisDoor in TheDoors
		if ThisDoor is not in ClosedDoors then
			set end of OpenDoors to (ThisDoor as string)
		end if
	end repeat
	set OpenDoor to some item in OpenDoors
	set NewDoors to {}
	repeat with ThisDoor in TheDoors
		if (ThisDoor as string) is not OpenDoor then
			set end of NewDoors to (ThisDoor as string)
		end if
	end repeat
	set TheString to "You chose " & ChosenDoor & return & return & "Now Monty has opened " & OpenDoor & return & return & "You may now choose to change your mind or stay with " & ChosenDoor & return & return
	set FinalDoor to (choose from list NewDoors with title "Monty Hall – Second Chance" with prompt TheString)
	set FinalDoor to item 1 of FinalDoor
	if FinalDoor = (ChosenDoor as string) then
		set TheString to "You chose to stay with " & ChosenDoor & return & return
	else
		set TheString to "You chose to change from " & ChosenDoor & " to " & FinalDoor & return & return
	end if
	if FinalDoor = WinningDoor then
		set TheWin to TheWin + 1
		set TheString to TheString & "Congratulations. " & FinalDoor & " is correct" & return & "You won this round" & return & return & "You have won " & TheWin & " and lost " & TheLoss
	else
		set TheLoss to TheLoss + 1
		set TheString to TheString & "Sorry. " & FinalDoor & " is not correct" & return & "THE WINNING DOOR WAS " & WinningDoor & return & "You didn't win this round" & return & return & "You have won " & TheWin & " and lost " & TheLoss
	end if
	set ThisChoice to display dialog TheString buttons {"STOP PLAYING", "PLAY AGAIN"}
	set ThisButton to button returned of ThisChoice
	if ThisButton is "PLAY AGAIN" then
		my mgPlay(TheDoors, TheWin, TheLoss)
	else
		display dialog "Thanks for playing" buttons {"OK"}
	end if
end mgPlay

But to see a definite pattern in the results, we need to run hundreds of games. And the process above is just too tedious. If we could automate the entire process, we could run 10,000 games in about a second.

First (see below) we set up four counters to keep track of our success rate. The next two lines are two lists to represent the contestant’s possible choices. The next two lines access the first list to randomly select a Winning Door and the contestant’s Chosen Door. This is what some item means. It means choose a random entry from any given list. The last line puts those results into a list of doors which must remain closed for the second part of the game. This list might contain only one door if the Winning Door is the same as the Chosen Door.

set NoChangeWin to 0
set NoChangeLose to 0
set YesChangeWin to 0
set YesChangeLose to 0

set TheDoors to {"Door 1", "Door 2", "Door 3"}
set TheChoices to {"Change Choice", "Don't Change Choice"} 

set WinningDoor to some item in TheDoors
set ChosenDoor to some item in TheDoors

set ClosedDoors to {WinningDoor, ChosenDoor}

The next section chooses a door to open. Remember, this cannot be the Winning Door, nor can it be the contestant’s Chosen Door. So first we put all the possible doors to open into a list (this list could contain either one or two doors), then choose a random door from that list to open.

Then the next section puts the two remaining closed doors into a list for the contestant to choose from again.

set OpenDoors to {}
repeat with ThisDoor in TheDoors
	if ThisDoor is not in ClosedDoors then
		set end of OpenDoors to (ThisDoor as string)
	end if
end repeat
set OpenDoor to some item in OpenDoors

set NewDoors to {}
repeat with ThisDoor in TheDoors
	if (ThisDoor as string) is not OpenDoor then
		set end of NewDoors to (ThisDoor as string)
	end if
end repeat

The next section is just a little more complicated. First we get a random choice from that second list from the start of the script. This represents whether or not the contestant changes their mind about which door they want. Then we use some nested if statements to determine the results.

Firstly, if the choice is to NOT change, then we set the Final Door to be the same as the original Chosen Door. Then we check to see if the Final Door is the same as the Winning Door randomly selected at the start of the script. If it is, we add 1 to the NoChangeWin counter. If it’s not the same, we add 1 to the NoChangeLose counter.

The second part below – the else statement – is for when the contestant DOES choose to change doors. We use a quick repeat loop to select the other door from the list of two closed doors to be our Final Door. Then run the same analysis as above. If the Final Door is the same as the Winning Door, we add 1 to the YesChangeWin counter. If it’s not the same, we add 1 to the YesChangeLose counter.

set TheChoice to some item in TheChoices
if TheChoice is "Don't Change Door Choice" then
	set FinalDoor to ChosenDoor
	if WinningDoor = FinalDoor then
		set NoChangeWin to NoChangeWin + 1
	else
		set NoChangeLose to NoChangeLose + 1
	end if
else
	repeat with ThisDoor in NewDoors
		if (ThisDoor as string) is not ChosenDoor then
			set FinalDoor to (ThisDoor as string)
		end if
	end repeat
	if WinningDoor = FinalDoor then
		set YesChangeWin to YesChangeWin + 1
	else
		set YesChangeLose to YesChangeLose + 1
	end if
end if

The final part is our reporting. This simply creates four lines of text with the various outcomes (win after not changing, lose after not changing, win after changing, lose after changing)

return "Contestant wins after NOT changing : " & NoChangeWin & return & "Contestant loses after NOT changing : " & NoChangeLose & return & return & "Contestant wins after changing  : " & YesChangeWin & return & "Contestant loses after changing  : " & YesChangeLose

Now all we need to do is put the whole lot together and include a repeat loop to generate as many games as we please. I found that 1,000,000 games takes about 27 seconds to run. But that’s a bit ridiculous because you don’t really need more than 10,000 games to show the pattern.

Here is the complete script with commenting included. Plus, below that, a sample of the output for 10,000 games.

--these are counters to track wins/losses
set NoChangeWin to 0
set NoChangeLose to 0
set YesChangeWin to 0
set YesChangeLose to 0

-- this is the list of possible doors to choose at the start
set TheDoors to {"Door 1", "Door 2", "Door 3"}

-- this is the list of possible contestant choices at the end
set TheChoices to {"Change Choice", "Don't Change Choice"}

repeat 10000 times
	-- randomly select a winning door
	set WinningDoor to some item in TheDoors
	
	-- randomly select the contestant's chosen door
	set ChosenDoor to some item in TheDoors
	
	-- put these into a list (they might be the same door)
	set ClosedDoors to {WinningDoor, ChosenDoor}
	
	-- randomly select a door to open
	-- it can't be the winning door, nor the contestant's chosen door
	set OpenDoors to {}
	repeat with ThisDoor in TheDoors
		if ThisDoor is not in ClosedDoors then
			set end of OpenDoors to (ThisDoor as string)
		end if
	end repeat
	set OpenDoor to some item in OpenDoors
	
	-- set the NEW list of possible doors to choose 
	-- this is a list of two doors (now that one door is open)
	set NewDoors to {}
	repeat with ThisDoor in TheDoors
		if (ThisDoor as string) is not OpenDoor then
			set end of NewDoors to (ThisDoor as string)
		end if
	end repeat
	
	-- randomly select if contestant will change their choice or not change
	-- and count how often contestant wins vs loses
	set TheChoice to some item in TheChoices
	if TheChoice is "Don't Change Choice" then
		set FinalDoor to ChosenDoor
		if WinningDoor = FinalDoor then
			set NoChangeWin to NoChangeWin + 1
		else
			set NoChangeLose to NoChangeLose + 1
		end if
	else
		repeat with ThisDoor in NewDoors
			if (ThisDoor as string) is not ChosenDoor then
				set FinalDoor to (ThisDoor as string)
			end if
		end repeat
		if WinningDoor = FinalDoor then
			set YesChangeWin to YesChangeWin + 1
		else
			set YesChangeLose to YesChangeLose + 1
		end if
	end if
end repeat

return "Contestant wins after NOT changing : " & NoChangeWin & return & "Contestant loses after NOT changing : " & NoChangeLose & return & return & "Contestant wins after changing  : " & YesChangeWin & return & "Contestant loses after changing  : " & YesChangeLose
Contestant wins after NOT changing : 1663
Contestant loses after NOT changing : 3353

Contestant wins after changing  : 3330
Contestant loses after changing  : 1654

If you run this script for 10,000 games over and over again you’ll see very similar results. That is, you will win the Monty Hall game approximately twice as often if you change your choice when presented with the two doors.

Now this may seem counter-intuitive (it did for me) because, surely, if you’re presented with only two doors, you’ve got a fifty-fifty chance of picking the right door. I mean, there’s just two doors, so it shouldn’t really matter which one you choose … right?

Well, yes, that would be true if you had not already been presented with the three doors.

Here’s how it works …

You’re presented with three doors and you choose one. Let’s just say you choose the first door.

If you choose the first door, all the possible scenarios for the winning door look like this (below). And the same is the case if you choose the middle door or the last door – no matter which door you choose, there are three possibilities for the winning door.

Then Monty Hall opens one of the doors. In the first scenario, there’s a choice of two doors to open. In the other two, there’s only one door that could be opened.

But the kicker is this … you don’t know which of the three scenarios is in play. But looking at this diagram it should be obvious that out of the three possible scenarios, only one sees you winning if you keep the same door. Whereas there are two possible scenarios where you will win if you change.

Of course, this does not guarantee you a win if you change doors. But, on average, you’ll win twice as often if you change.

Keep grunting

macgrunt icon

Advertisements

InDesign scripting : lesson 36

throughout this site (and others) you’ll find lots of applescripts which are direct responses to real-world workflows. but, chances are, you’ll need to do some cut-pasting, and additional code writing to create a script which does exactly what you want in your workflow.

this can be a bit intimidating and not a little frustrating as you try to get the bloody thing to work.

so here’s a post (and there may be more to follow) which gets back to basics and addresses a common beginner question :

When I’m cutting and pasting sections of a script, how many ‘end tells’ do I need to include?

new scripters will often be confronted with errors like these when trying to compile or run a script in development :
end error 01
end error 02

one of the main problems with this kind of error, as compared with many other types of errors, is that Applescript Editor may not highlight the exact line where the problem needs to be fixed.

the important thing to remember is that the number of ‘end tells’ will be exactly the same as the number of ‘tells’.

when writing code from scratch, it’s best practice to write the ‘tell’ and ‘end tell’ first — then go back and fill in the rest of the code :

tell application id "com.adobe.InDesign"
-- rest of code here
end tell

you can have tells within tells (or nested tells) — these get closed off in the reverse order that they were opened — but notice, still, the number of ‘end tells’ is the same as the number of ‘tells’ :

tell application id "com.adobe.InDesign"
  -- code here addresses the application
  tell active document
    -- code here addresses the active document
    tell layout window 1
      -- code here addresses the layout window
    end tell
    -- code here addresses the active document
  end tell
  -- code here addresses the application
end tell



other commands
this is also the case with other commands that need an ‘end’ statement. for example, if you have an ‘if’ command within a ‘tell’ command, the ‘if’ needs to end before the ‘tell’ ends :

tell application id "com.adobe.InDesign"
  if x = y then
    -- some more code
  end if
end tell

when writing code you can also close off with just ‘end’ and the editor will work out which end goes with which command when the script is compiled. so you can write it out like this :

tell application id "com.adobe.InDesign"
tell active document
if x = y then
repeat 5 times
-- some more code
end
end
end
end

… and, when compiled, it will become this :

tell application id "com.adobe.InDesign"
  tell active document
    if x = y then
      repeat 5 times
        -- some more code
      end repeat
    end if
  end tell
end tell

but you can see that even here — all the commands get closed down in the reverse order that they were opened. the ‘repeat’ is closed first, then the ‘if’, then the second ‘tell’ and lastly the first ‘tell’.



… and another thing
whenever possible, it’s best to avoid one application ‘telling’ another application :

tell application id "com.adobe.InDesign"
  set mgFolder to file path of active document
  tell application "Finder"
    set mgOLDFolder to (mgJobFolder & "OLD:" as string) as alias
  end tell
end tell

… is better writen as :

tell application id "com.adobe.InDesign"
  set mgFolder to file path of active document
end tell
tell application "Finder"
  set mgOLDFolder to (mgJobFolder & "OLD:" as string) as alias
end tell

… where we close the InDesign tell before opening the Finder tell



are there other scripting issues that keep cropping up for you?

macgrunt icon

InDesign scripting : lesson 35

miracles happen

macgrunt has now received at least two undeniable confirmations of the medicinal properties of applescript : the first was from a generous commenter on one of the earlier lessons which you can read here ; and the second was from a user who asked for assistance with a different exporting problem (which is what this post will be about, eventually). to quote :

The joy also helped ease my sciatic nerve pain I have been suffering for more than a month now.

now, the cynical among you may think that these people are exaggerating or just being flippant — but do you really have a good and verifiable reason to doubt the written testimony of fellow mac users? not likely.

here’s the brief for the sciatica-easing workflow : export the active document to 300ppi CMYK jpegs and 150ppi RGB pngs.

the first part of the script is pretty similar to all the other exporting scripts on this site — get the filename and path of the active document, shorten the filename so that the names of the exported images won’t be too long, create some folders to hold the exported images :

-- developed by macgrunt for use with InDesign
-- last updated April 2014
-- macgrunt.com

-- export jpgs and pngs from active InDesign document

tell application id "com.adobe.InDesign"
  set mgFolder to file path of active document
  set mgDocName to name of active document
end tell

----------------------------------
-- this bit shortens filename to first space
set text item delimiters of AppleScript to {" "}
set mgDocName to text item 1 of mgDocName
set text item delimiters of AppleScript to ""
----------------------------------

----------------------------------
-- this bit creates subfolders in the same location as the InDesign file
tell application "Finder"
  if (exists folder "JPGs" of folder mgFolder) is false then
    set mgJPGfolder to make new folder at mgFolder with properties {name:"JPGs"}
  else
    set mgJPGfolder to (mgFolder & "JPGs") as string
  end if
  set mgJPGfolder to mgJPGfolder as alias
  if (exists folder "PNGs" of folder mgFolder) is false then
    set mgPNGFolder to make new folder at mgFolder with properties {name:"PNGs"}
  else
    set mgPNGFolder to (mgFolder & "PNGs") as string
  end if
  set mgPNGFolder to mgPNGFolder as alias
end tell
----------------------------------

hopefully it’s clear that mgFolder is where the InDesign file is saved, so the new JPGs and PNGs folders are created in that same folder. these new folders are created using an if/then/else routine — if you just tried to create the new folder when there was already one there, the script would throw an error — and that’s tedious.

the second half of the script sets the export parameters and does the actual exporting — pretty simple :

tell application id "com.adobe.InDesign"
  ----------------------------------
  -- here is where we set the export parameters
  set Exporting Spread of JPEG export preferences to false
  set JPEG export range of JPEG export preferences to export range
  set JPEG Quality of JPEG export preferences to maximum
  set jpeg color space of JPEG export preferences to CMYK
  set export resolution of JPEG export preferences to 300
  
  set Exporting Spread of PNG export preferences to false
  set PNG export range of PNG export preferences to export range
  set PNG Quality of PNG export preferences to maximum
  set PNG color space of PNG export preferences to RGB
  set export resolution of PNG export preferences to 150
  ----------------------------------
  
  repeat with mgCount from 1 to (count pages of active document)
    set mgCount to mgCount as string
    set Page String of JPEG export preferences to mgCount
    set Page String of PNG export preferences to mgCount
    set mgJpgFilePath to (mgJPGfolder & mgDocName & "_" & mgCount & ".jpg") as string
    set mgPNGFilePath to (mgPNGFolder & mgDocName & "_" & mgCount & ".png") as string
    tell active document
      export format JPG to mgJpgFilePath without showing options
      export format PNG format to mgPNGFilePath without showing options
    end tell
  end repeat
  
  display dialog "ALL DONE!" buttons {"Okey-dokey"} default button 1
end tell

this block of code highlights one of the frustrating things about trying to write applescript — the inconsistency of syntax. notice how ‘jpeg color space’ is not capitalised like all the other export preference properties? that’s not really a big deal because the script editor will correct that automatically if you accidentally type it in caps.

but notice the two export commands : ‘export format JPG’ vs ‘export format PNG format’ — why do they have different structures? who knows — adobe are just weird. it’s even weirder when you include the two PDF export commands : ‘export format PDF type’ vs ‘export format interactive PDF’. best not to think about it.

that repeat loop at the end is purely to get consistency of filenaming. if we just export a document “mgThis_file.indd” to jpegs, the filenames would be “mgThis_file.jpg”, “mgThis_file2.jpg”, “mgThis_file3.jpg”, etc. again with the inconsistency … why doesn’t InDesign add ‘1’ to that first jpg? — adobe really do have some serious explaining to do.

anyway, as we were adding a repeat loop to include ‘1’ on the first image, we took the opportunity to add an undescore as well. so these files would now come out as “mgThis_file_1.jpg”, “mgThis_file_2.jpg”, “mgThis_file_3.jpg”, etc.

obviously, this is a very basic script which exports a single document at a time, using one set of export preferences. but like all the PDF export scripts on this site, it doesn’t take much to adapt it to export all open documents, or give the user the chance to choose export preferences.

keep grunting.

macgrunt icon

InDesign scripting : lesson 34

ok, here’s the last lesson (for now) on exporting each page of an InDesign file as a separate pdf using applescript. lesson 32 showed a basic solution and lesson 33 expanded it to allow for more naming options. this lesson will look at how to generate the pdf filenames from information saved in the InDesign file itself. this solution was prompted by comment 39 in this discussion :

I’m using InDesign to manage 200+ pages of tech sheets for a company … The obvious benefit of using InDesign and having all the tech sheets in a single file is because then I can edit and update Master Pages and have the changes reflected in all of the tech sheets. The major downside though is that when exporting the PDFs … the file name must be specified manually for each tech sheet.

as always, there are many ways to skin a cat. we’re going to look at two approaches — naming based on the heading text of each page, and naming based on script labels. both solutions rely on setting up the InDesign file in a particular way and both solutions begin with this basic setup (refer to the previous lessons for an explanation of these parts of the script) :

tell application id "com.adobe.InDesign"
  set raster resolution of interactive PDF export preferences to one hundred forty four ppi
  set (export reader spreads of interactive PDF export preferences) to false
  set mgFolder to file path of active document
  set mgDocName to name of active document
end tell

tell application "Finder"
  if (exists folder "the pdfs" of folder mgFolder) is false then
    make folder at mgFolder with properties {name:"the pdfs"}
  end if
end tell

tell application id "com.adobe.InDesign"
  repeat with x in pages of active document
    set mgPageName to name of x
    set page range of interactive PDF export preferences to mgPageName
    
    tell active document

      -- REST OF FUNCTIONALITY WILL GO IN HERE

      set mgFilePath to mgFolder & "the pdfs:" & mgText & ".pdf" as string
      export format interactive PDF to mgFilePath without showing options
    end tell
    
  end repeat
  display dialog "ALL DONE!" buttons {"OK"} default button 1
end tell

here’s a snippet of our InDesign file :
screen grab of tech sheets indesign file

there are a few different approaches you could use to pull out the heading on each page as the filename for each pdf. but the easiest way is to set up the InDesign file so that those heading text frames are on their own layer (for this example we’ve called the layer ‘header layer’). so, each page has only one text frame on the header layer — the heading. here’s the remainder of the code to be plugged into the basic script above :

set mgFrames to (text frames of x whose name of item layer is "header layer")
set mgFrame to item 1 of mgFrames
set mgText to contents of item 1 of mgFrame

the first line makes a list of all the text frames on the page on the header layer (this should be a list with only one item). the second line references the first (and only) item in the list — the heading text frame. and the third line grabs the ‘contents’ of that frame (the heading text) to use as the pdf filename. running that completed script will give you this result :
screen grab of generated pdfs
simple, eh?

the alternative method allows you to specify a filename for each page without that filename actually appearing anywhere on the page — the invisible script label. script labels are awesome and were the key to the functioning of the calendars script. a script label can be assigned to any page item, but for this example we’re going to label the heading text frames.

you open the script label panel under windows > utilities. select the frame you want to label — then just type the new label into the panel. once you’re done, deselect the frame — don’t just go to a different page, the script label won’t stick — and don’t hit ‘return’, as this will become part of the label :
screen grab of script label panel

providing you have only one labelled item on each page, you only need to add one extra line to that original basic script above :

set mgText to get (label of every page item of x whose label is not "")

giving you these pdfs :
screen grab of second set of pdfs

another variation will also add the export date to the pdf filenames — this is a good way to keep track of which version you’re up to. we just need to add two more lines :

set mgDate to do shell script "date '+%d-%m-%y'"

and

set mgText to mgText & "_" & mgDate

so the finished script would look like this :
screen grab of final script to export by script labels

and would create pdfs like this :
screen grab of final dated pdfs

ok, so there we have it — a few variations on the exporting-each-page-as-a-separate-pdf theme. hopefully, from this brief introduction, you can see that there are any number of ways to get the job done with applescript. there’s a solution out there for just about any workflow.

keep grunting.

macgrunt icon

InDesign scripting : lesson 33

lesson 32 started looking at how to export individual pages as separate PDFs. it looks like this topic has become popular … a fellow aussie with a passion for getting the most out of InDesign, with a particular focus on prepress, has also written about this, outlining a few solutions — Breaking up is hard to do…

as colmin8r points out, the script from lesson 32 has no user interface, so you can’t control how files are named. that’s what this lesson is all about — extending the script to give you more options. this is how scripts develop — get the basic functionality working, then expand the script to make it more user-friendly, or adaptable, or whatever.

here’s our base script — this time we’re exporting an interactive PDF, rather than using a print PDF preset — but apart from that, it’s the same as the script from lesson 32 :

tell application id "com.adobe.InDesign"
  set raster resolution of interactive PDF export preferences to one hundred forty four ppi
  set (export reader spreads of interactive PDF export preferences) to false
  set mgFolder to file path of active document
  set mgDocName to name of active document
end tell

tell application "Finder"
  if (exists folder "the pdfs" of folder mgFolder) is false then
    make folder at mgFolder with properties {name:"the pdfs"}
  end if
end tell

tell application id "com.adobe.InDesign"

  -- REST OF FUNCTIONALITY WILL GO IN HERE

  repeat with x from 1 to count pages of active document
    set mgPageName to name of page x of active document
    set page range of interactive PDF export preferences to mgPageName
    set mgFilePath to mgFolder & "the pdfs:" & mgDocName & mgPageName & ".pdf" as string
    tell active document
      export format interactive PDF to mgFilePath without showing options
    end tell
  end repeat
  display dialog "ALL DONE!" buttons {"OK"} default button 1
end tell

we’re going to give the user two options — shortening the existing filename by breaking it at the first space or underscore, and the option to add something else to the filename. the code for the dialog looks like this :

  set mgDialog to make dialog
  tell mgDialog
    tell (make dialog column)
      tell (make dialog row)
        make static text with properties {static label:"Where do you want to break the filename?"}
      end tell
      tell (make dialog row)
        set mgDelimiterButtons to make radiobutton group
        tell mgDelimiterButtons
          make radiobutton control with properties {static label:"first underscore", checked state:true}
          make radiobutton control with properties {static label:"first space"}
          make radiobutton control with properties {static label:"full stop", min width:290}
        end tell
      end tell
      tell (make dialog row)
        make static text with properties {static label:"current filename : " & mgDocName}
      end tell
      tell (make dialog row)
        make static text with properties {static label:" "}
      end tell
      tell (make dialog row)
        make static text with properties {static label:"Enter the text you'd like to append."}
      end tell
      tell (make dialog row)
        set mgSuffixField to make text editbox with properties {edit contents:"(can be blank)", min width:250}
      end tell
    end tell
  end tell
  set mgResult to show mgDialog

… and creates a user interface that looks like this :
screen grab of initial user interface
a couple of things to notice … we’ve also added the option of breaking the filename at the first full stop which, if you’re naming your files correctly, means not shortening the filename at all. the dialog also shows the existing filename (in this case “export separate pages_test_file.indd”) to make it easier for the user to make their selections.

the remainder of the code takes the information from the user interface and constructs a new filename to use when saving the PDFs :

  if mgResult is true then
    set mgDelimiterButton to selected button of mgDelimiterButtons
    if mgDelimiterButton is 0 then
      set mgDelimiter to "_"
    else if mgDelimiterButton is 1 then
      set mgDelimiter to " "
    else if mgDelimiterButton is 2 then
      set mgDelimiter to "."
    end if
    
    set text item delimiters of AppleScript to mgDelimiter
    set mgDocName to text item 1 of mgDocName
    set text item delimiters of AppleScript to ""
    
    set mgSuffix to edit contents of mgSuffixField as string
    if mgSuffix is "(can be blank)" then
      set mgSuffix to ""
    end if
    set mgDocName to (mgDocName & "_" & mgSuffix)
    destroy mgDialog
  else
    error number -128
  end if

two things to note here. we’ve added some error handling just in case the user inadvertently leaves “(can be blank)” in the text field of the user interface — we don’t want that to become part of the filename, so we change it to “” (that is, nothing) instead. also, we’re using an underscore to join the shortened filename and the suffix.

hopefully it’s pretty clear how this filenaming works. as an example — if we start with the filename “export separate pages_test_file.indd”, break it at the first space and add the suffix “concepts”, our resulting PDF filenames will be “export_concepts1.pdf”, “export_concepts2.pdf”, etc.

ok, that’s one way you could do it — but you could also make it more complex again by giving the user the option to break the filename in a different place. of course, more complex options mean more complex code. so this :

  set mgDialog to make dialog with properties {name:"Where do you want to break the filename?"}
  tell mgDialog
    tell (make dialog column)
      set mgPlaceButtons to make radiobutton group
      tell mgPlaceButtons
        make radiobutton control with properties {static label:"first", checked state:true}
        make radiobutton control with properties {static label:"second"}
        make radiobutton control with properties {static label:"third", min width:100}
      end tell
    end tell
    tell (make dialog column)
      set mgDelimiterButtons to make radiobutton group
      tell mgDelimiterButtons
        make radiobutton control with properties {static label:"space", checked state:true}
        make radiobutton control with properties {static label:"underscore"}
        make radiobutton control with properties {static label:"full stop", min width:130}
      end tell
    end tell
    tell (make dialog column)
      tell (make dialog row)
        make static text with properties {static label:"current filename : "}
      end tell
      tell (make dialog row)
        make static text with properties {static label:mgDocName}
      end tell
      tell (make dialog row)
        make static text with properties {static label:" ", min width:300}
      end tell
      tell (make dialog row)
        make static text with properties {static label:"Enter the text you'd like to append."}
      end tell
      tell (make dialog row)
        set mgSuffixField to make text editbox with properties {edit contents:"(can be blank)", min width:250}
      end tell
    end tell
  end tell
  set mgResult to show mgDialog

is what you need to create a user interface that looks like this :
screen grab of revised user interface

and the rest of the code is more complex too. this time we’re using the chosen break character to join the filename to the suffix (rather than forcing it to be an underscore) :

  if mgResult is true then
    set mgPlaceButton to selected button of mgPlaceButtons
    if mgPlaceButton is 0 then
      set mgPlace to "1"
    else if mgPlaceButton is 1 then
      set mgPlace to "2"
    else if mgPlaceButton is 2 then
      set mgPlace to "3"
    end if
    set mgDelimiterButton to selected button of mgDelimiterButtons
    if mgDelimiterButton is 0 then
      set mgDelimiter to " "
    else if mgDelimiterButton is 1 then
      set mgDelimiter to "_"
    else if mgDelimiterButton is 2 then
      set mgDelimiter to "."
    end if
    
    set text item delimiters of AppleScript to mgDelimiter
    try
      set mgDocName to text items 1 thru (mgPlace + 1) of mgDocName
      set mgDocName to text items 1 thru mgPlace of mgDocName
    on error
      display dialog "come on dude — that's not possible" & return & "there are not " & mgPlace & " \"" & mgDelimiter & "\" in the filename" & return & return & "have another go" buttons "sorry"
      error number -128
    end try
    set mgDocName to mgDocName as string
    set text item delimiters of AppleScript to ""
    
    set mgSuffix to edit contents of mgSuffixField as string
    if mgSuffix is "(can be blank)" or mgSuffix is "" then
      set mgSuffix to ""
      set mgDocName to (mgDocName & mgSuffix)
    else
      set mgDocName to (mgDocName & mgDelimiter & mgSuffix)
    end if
    destroy mgDialog
  else
    error number -128
  end if

so this time — if we start with the filename “export separate pages_test_file.indd”, break it at the second space and add the suffix “concepts”, our resulting PDF filenames will be “export separate concepts1.pdf”, “export separate concepts2.pdf”, etc.

notice in the above code we’ve added some error handling to make sure the user’s choices make sense. that is, if they choose to break the filename at the third space, and there are not three spaces in the filename, rather than the script simply failing, they’ll get a message stating why it’s not going to work :
screen grab of error message presented to user

and, of course, you can keep on expanding the script until you have as many different options as suits your needs.

the next lesson will look at a couple of other ways we could approach the naming of these separate page PDFs — automatically generating filenames based on data already existing in the document.

until then, keep grunting.

macgrunt icon