Sunday, March 6, 2011

Great resource for users of both Python and Applescript...

Basically, Applescript is a propriteary language which only serves one purpose: to have the official interface to the OSA on Mac OS X. Using applescript for any other purpose, really is not a great idea.

Python, on the other hand, might be considered the de-facto alternative to applescript on the mac when it comes to OSA. you can use the program appscript which is a bridge to applescript from Python. If your interested in using a modern programming language built from the ground up to be readable, understandable, writable, and has a small lerning curve (all the things necessary for a successful scripting language) then Python is basically the best choice.

Here's a great website to get you used to how to use Python, especially coming from Applescript. Its a handy cross reference. Its also a great reference for Python users who find themselves needing to inevitably write a few lines of applescript.

http://aurelio.net/doc/as4pp.html

Tuesday, October 5, 2010

Applescript GUI Scripting Tips and Tricks...

Hi, I found some new tips and tricks for applescript GUI scripting that I think everyone should be able to figure out. I searched google everywhere and finally found some hints here and there but very difficult to find.

The first thing I wanted to do was to check to see if GUI Scripting (aka assistive devices) was enabled in the system. This is required if you want to use any gui scripting (ie, everyone doing automation) so here's the code I ended up using. This function will check to see if assistive devices is enabled, and loop to enable it asking the user. If the setting doesn't take (due to user typing in their password incorrectly, or other reason) it will reloop until either success or until you click cancel:

    on enableGUIScripting()
        tell application "System Events"
            set currentstate to UI elements enabled
            repeat while currentstate = false
                tell application "Finder"
                    activate
                    display dialog "Assistive devices must be enabled: enable now?"
                    delay 0.5
                end tell
                activate
                set UI elements enabled to true
                set currentstate to UI elements enabled
            end repeat
        end tell
        return currentstate
    end enableGUIScripting

Another thing I wanted to do and you will need to be able to do if your programming automation routines in applescript is to enable full keyboard control. This allows the "TAB" key to access all controls on a page rather than just text input fields. This is located under System Preferences -> Keyboard -> Keyboard Shortcuts -> Full Keyboard Access :
    on enableFullKeyboardControl()
        set currentstate to ""
        do shell script "defaults read .GlobalPreferences AppleKeyboardUIMode"
        set currentstate to the result
        repeat while currentstate is not 2
            tell application "Finder"
                activate
                display dialog "Full Keyboard Access must be enabled: enable now?"
                delay 0.5
            end tell
            do shell script "defaults write .GlobalPreferences AppleKeyboardUIMode -int 2"
            do shell script "defaults read .GlobalPreferences AppleKeyboardUIMode"
            set currentstate to the result
        end repeat
        return currentstate
    end enableFullKeyboardControl

Thanks to a couple websites for their help:
Assistive devices enable:
http://www.peachpit.com/blogs/blog.aspx?uk=Using-AppleScript-to-enable-GUI-Scripting-Five-AppleScript-Tips-in-Five-Days

Full Keyboard Control info:
https://secure.macscripter.net/viewtopic.php?id=25417

Monday, September 27, 2010

ApplescriptObjC tutorial: the most basic tutorial

I wrote a collection of Applescripts to automate data entry and other processes. These scripts however don't have a good place to live. For example, how should I launch them all? Double click on their icon? I now have about 10 scripts and I need a way to launch them. I decided to go the route of AppleScriptObjC. This is a brand new platform Apple designed to replace Applescript Studio. It is essentially applescript bindings to the cocoa library. The only reason this is useful is because the XCode Interface Builder (IB) allows you to design your own window by drag and drop of buttons, etc. Each button can correspond to launching a script. You can include text fields to gather user intput, and you can even implement progress bars, etc... But lets not get ahead of ourselves. The problem with ApplescriptObjC is that it requires a basic understanding of Cocoa. But, I believe that is only because nobody on the net is currently talking about it from the scripter's perspective.

If you start out as someone wanting to write automation routines, then why would you ever consider writing or learning C libraries? Nobody is doing that this day and age. Its bad enough you have to use applescript in the first place.

This tutorial is for those of you who don't want to program C libraries, but want to write your automations and hook them into a GUI you build using drag and drop. I'll start by opening a new project and creating a simple program that has 1 button and when you click it it runs your script.


Phase 1: Writing the code.
  1. Download and install Apple's XCode (requires snow leopard 10.6 or better)
  2. Start a new project by selecting "Cocoa Applescript Application" and name it "test"
  3. Once your project is created and in front of you look on the left side for "Classes" go into it and select "testAppDelegate".  This is simply a file containing all your applescripts. You can safely ignore everything else for now.
  4. Notice there is a script in there: "script testAppDelegate" this is the script where you will setup functions that will run when you click buttons in your program. Notice the first line is: "property parent : "NSObject"". This is inheritance. Everything in this script inherits all the functions available in the cocoa class NSObject. Fortunately we don't care what this means or does as we just want to setup a GUI for our collection of scripts. Just keep in mind that everything you implement needs to go after this for cocoa to work properly.
  5. Create a new function call it "ButtonClicked" and SAVE:
 on ButtonClicked_(sender)
display dialog "button was clicked"
end ButtonClicked_
You need to have the underscore at the end of the function name and the parameter must be sender. This informs the Cocoa libraries that it can accept connections from buttons you will later create in the Interface Builder (IB)

Phase 2: Designing the window and connecting buttons.
  1. Next on the left side under resources: double click on the file MainMenu.xib This will open up the interface builder and start you out with a default empty window.
  2. If the library pane isn't already opened up, click on "Tools>Library" and the window should open. Scroll down until you find a button and drag it into your main window.
  3. Type whatever text you want into the button to label it
  4. Control-Drag the button to the object "Test App Delegate" in the MainMenu.xib object browser. It should create a line between the button and the App Delegate object  (your script)
  5. When you let go of the mouse click, it should popup a context HUD menu. Select "ButtonClicked:". If it is not an option go back to the app delegate script and ensure you have it saved and ensure that it is setup correctly with the underscore and parameter "sender" as described above. Notice they use a : instead of _ in the name of your function that is because Cocoa is converting the functionname syntax from applescript to C. Don't worry about that it is supposed to be that way.
  6. You are now fully connected. SAVE from within MainMenu.xib's object browser and go back to XCode and hit the hammer button to compile and run.
A couple of notes: notice that when you click the button, it doesn't become available again until the script finishes running. So if you have a script that requires lots of time to run and you want to continue to use your application you might consider forking it off using a shell script call to osascript. I have another post that describes exactly how to do that here

forking a thread in AppleScript...

I searched everywhere to learn how to fork a background thread or process using Applescript. The best I could find is to use "do shell script" to start an osascript background process to run the second script. This works pretty well so long as your script works properly under osascript.

In my particular case, I was using Applescriptobjc to write a GUI for my collection of applescripts that assist data entry in a proprietary database I use at work.  Then I wanted a background process to control the visibility of this application depending on whether or not that program is the active window. The script then moves the position of the window to the bottom of that application's window.

The first option was to just have a separate applescript that would run separately. But now that I can fork a thread from within the main program, there is no need to do this. Here's the code I used:

    on DockUtility()
        set pathname to "/Applications/lightspeedhelper/"
        set scriptname to pathname & "StickToLightSpeed.scpt"
        set logfile to pathname & "StickToLightSpeed.log"
        set bgPID to ""
       
        do shell script "osascript " & scriptname & " &> " & logfile & " & echo $!" -- redirect output and send as bg thread
        set bgPID to the result
    end DockUtility

the shell script evaluates to:

osascript scriptname.scpt &> logfile.log & echo $!

This will launch osascript from a unix shell, redirect standard output and standard err to the log file (that is what you have to do for it to launch in the background) and the ampersand at the end means set it to launch in the background. The echo $! is a shell command that returns the process ID (PID) of this process. It is useful sometimes to have the bgPID there so you can later kill the process or check if it is active.