Tag Archive: “code”

Minimal Tcl/Tk Mac Application Packages

Here is one way to create a self-contained double-clickable Tcl/Tk application for Mac OS X.

Code

Save this text as hello.tcl:

package require Tk
pack [label .l -text "Hello, World!" -padx 30 -pady 30]

For clarity, save this and other example files to the Desktop.

Tclkit

Download and unzip 8.4.16/tclkit-darwin-univ-aqua from the Tclkit download matrix. This is a universal binary version of the latest stable release of Tcl/Tk with native Mac OS X interface elements.

Update: On Mac OS X 10.5 (“Leopard”), at least with current X11 patches, you can use non-Aqua Tclkits as the basis for Mac application packages. X11 will open automatically. Since Tk Aqua doesn’t play well with Leopard, this may prove to be a useful (if ugly) interim solution.

Updatier: The Leopard compatibility problems alluded to above appear to be resolved in Tcl/Tk 8.4.17. Tclkits of 8.4.17 and the official 8.5.0 release are now available.

Updatiest: You can now build your own tclkits with ease using the Kitgen Build System.

SDX

Download sdx.kit from the SDX web page. SDX is a utility that can, among other things, create executable Starpacks by joining Tclkit interpreters with your Tcl code. (Safari may warn that the file is executable and append .sh to its name; just remove the .sh after downloading.)

Files

Open Terminal and change to the directory containing these files:

cd ~/Desktop

Make an executable copy of the Tclkit.

cp tclkit-darwin-univ-aqua tclkit
chmod +x tclkit

The duplicate is needed because Tclkit is used both to execute SDX and as input to SDX. It cannot otherwise operate on itself.

Starpack

Create the Starpack by “quick wrapping” the sample code:

./tclkit sdx.kit qwrap hello.tcl -runtime tclkit-darwin-univ-aqua

This yields the executable file hello, which you can run from the command line:

./hello

Different Tclkits can be used to create executables for different platforms. Omitting the -runtime argument creates a platform-independent Starkit which can be executed by an external Tclkit. Instead of qwrap, more complex projects can use the wrap command to package their entire directory structure as a virtual filesystem.

Icon

Every application needs a good icon. This is not a good icon, but it will suffice as an example:

icon hello.icns

Img2icns is a handy utility for converting images to icns files.

Package

Create a folder called Hello World.app. Because the app extension designates an application, the Finder will treat the folder as an application package. Control-click the embryonic application and select “Show Package Contents”:

Show Package Contents contextual menu

Create a folder called Contents inside the package, and two folders titled MacOS and Resources within Contents. Drag hello.icns into Resources and the hello executable into MacOS.

Info

Every application contains a file that lists certain application properties. Because some properties identify the very components that comprise the application, the application won’t work without this vital file.

Here is an example Info.plist. Place this file in the package’s Contents folder.

Info.plist in Property List Editor

Note that the CFBundleExecutable property identifies the executable (found in MacOS) and that CFBundleIconFile identifies the application icon (found in Resources). The CFBundleIdentifier and CFBundleSignature properties should be unique to your application. The CFBundleGetInfoString, CFBundleShortVersionString, and CFBundleVersion properties specify your application’s version.

Consult Apple’s Property List Key Reference for more details about these and other possible application properties. As XML files, property lists can be edited with text editors or Apple’s dedicated Property List Editor (included with the developer tools).

Showtime

The application package should now be organized like this:

Hello World.app
 /Contents
    Info.plist
   /MacOS
      hello
   /Resources
      hello.icns

It may be necessary to temporarily rename the application in order for the Finder to recognize the new package information. Hello World.app will adopt the hello.icns icon. Give it a double-click:

final application screenshot

The default menu bar items and window title are provided by Tk. The internal main.tcl filename is introduced by qwrap.

So, that’s a tidy way to package a Tcl/Tk program for Mac OS X deployment. The application package is really just a few folders, an icon, and a property list wrapped around a Starpack. If you’re already using Starkits, you’re only a few steps away from delivering a relatively well-integrated Macintosh version of your application.


Credit

The technique described here is largely inspired by Kevin Walzer’s How to Build Tcl/Tk Application Bundles the Mac Way tutorial. The companion article on Tk Aquafication offers more guidance on integration with Macintosh interface conventions.

The example Info.plist is based on the property list included in the Tcl/Tk Aqua 8.4.16 standalone Wish Shell, which I cannot find listed on any relevant web sites. As with other recent releases, it seems to be available only by direct download from mailing list announcements.

Posted on Saturday, December 8th, 2007.

Chipmunk Basic Language Module

I’ve updated my Chipmunk Basic language module for TextWrangler/BBEdit. Named subroutines are now identified in the function popup menu.

Chipmunk Basic language module for TextWrangler with function popup

Download cbastw.plist.zip 2k (requires TextWrangler 2.2 or BBEdit 8.5)

Posted on Monday, November 12th, 2007.

Starkits and Starpacks

Tcl scripts are typically interpreted by the Tcl shell, tclsh.

Starkits are single files that contain a virtual filesystem populated with Tcl scripts and other files. Starkits are interpreted by Tclkits, which are single files containing entire Tcl installations.

A Starpack is a Starkit joined to a Tclkit. It is a single executable file that contains a script and the interpreter needed to run it.

Why

Packaging Tcl/Tk scripts as Starkits is advantageous because it allows the set of files that comprise an application to be distributed and installed as a single file.

Likewise, Tclkits allow the complex set of files that comprise a Tcl/Tk installation to be managed as a single file. This confers advantages to the developer (easily test against different interpreter releases) and to the user (installation is simple—as is uninstallation).

Because they have no prerequisites, Starpacks are ideal for distribution to casual users. Since each Starpack contains a generic Tclkit, expert users may opt to save space by installing a single Tclkit to execute individual application Starkits.

How

Tclkits come in many flavors. You can run a Starkit with a Tclkit of your choice by calling it explicitly:

./tclkit-darwin-univ Starkit.kit

If a Tclkit is installed as tclkit, it is used implicitly when you execute a Starkit directly:

cp tclkit-darwin-univ /usr/local/bin/tclkit
./Starkit.kit

Starpacks are executed directly:

./Starpack

The leading ./ characters imply that the executable is in the current directory.

Starkits and Starpacks can be created and disassembled with SDX, itself a Starkit. You can use SDX to create a Starpack of itself.

Posted on Monday, October 1st, 2007.

User Interfaces for ThisService Scripts

ThisService is arguably — and appropriately — oriented towards text-processing and other “faceless” services. However, with a little imagination it is easy to conjure uses for the basic user interaction capabilities provided by AppleScript’s Standard Additions. Incorporating these commands in a service script requires a little extra sleight of hand, as it may be necessary to direct them to whatever application is active when the service is invoked.

I have had variable success in my various attempts to do this. Here is a summary of how it can be done, combined with an account of problems I’ve encountered with certain configurations.

Finding the Name of the Frontmost Application

In his example word count service, John Gruber demonstrates how to use System Events to determine the name of the frontmost application:

tell application "System Events"
    set _app to the name of the first process whose frontmost is true
end tell

Another method discussed at the Mac OS X Hints forums also seems to work, even though the .app extension is included in the application name:

set _app to name of (info for (path to frontmost application))

Now the frontmost application (whatever it may be) can be directed to do something.

tell application _app to ...

This is useful because we don’t necessarily know what application will be active when our service is invoked.

An Interface for Input Services: A Predictable Success

ThisService can generate three kinds of scripts: those that act on input, those that produce output, and those that do both (filters). Gruber’s word count service acts on input, as does my save selection service. The AppleScript that powers these kinds of services looks something like this:

on process(_input)
    tell application "System Events" to set _app to the name of the first process whose frontmost is true
    tell application _app to display alert "Input: " & _input
end process

Example Input Service

When text is selected and the service is invoked, an alert reports the selected text.

An Interface for Output Services: A Disappointing Surprise

Here is the code for a simple service that produces output. Via the same mechanism as the input service, it attempts to report the name of the frontmost application and return that string:

on process()
    tell application "System Events" to set _app to the name of the first process whose frontmost is true
    tell application _app to display alert "Output: " & _app
    return _app
end process

Example Output Service

Running this service causes the frontmost application to become unresponsive for an abnormal length of time. Spin Control indicates that the service executable also hangs, although I don’t know how to interpret the report. The alert subsequently appears, but the service fails: the script’s return value is not inserted. A relevant message appears in the Console:

2007-09-12 21:45:28.621 TextEdit[2721] tossing reply message sequence 9 on thread 0x306a90

An Interface for Filter Services: Beating a Dead Horse

This filter service exhibits the same problem as the example output service.

on process(_input)
    tell application "System Events" to set _app to the name of the first process whose frontmost is true
    tell application _app to display alert "Input: " & _input & return & "Output: " & _app
    return _app
end process

Example Filter Service

The alert appears after an extended delay, but no output is received.

An Interface Compromise for Output and Filter Services

One way to circumvent the issue is to tell System Events to display the interface itself. This eliminates the delay and allows the service to return successfully.

on process()
    tell application "System Events"
        set _app to the name of the first process whose frontmost is true
        activate
        display alert "Output: " & _app
    end tell
    return _app
end process

Example Output Service Hack

Unfortunately, this approach has the unresolved side effect of stealing focus from the frontmost application’s windows. Explicitly restoring focus would require something like tell application _app to activate, which exhibits the problematic behavior.

on process(_input)
    tell application "System Events"
        set _app to the name of the first process whose frontmost is true
        activate
        display alert "Input: " & _input & return & "Output: " & _app
    end tell
    return _app
end process

Example Filter Service Hack

Without activate, System Event’s alert wouldn’t receive focus, either.

Inconclusive Conclusions

The method described above allows output and filter services to present simple user interfaces, but it is an imperfect and somewhat annoying solution. If you want to provide a robust interface for an arbitrary service, you should probably write the service from scratch. On the other hand, tell application _app’s compatibility with input services suggests the same idiom ought to be possible with the other kinds of services produced by ThisService.

It is also possible that I have overlooked or misunderstood some aspect of AppleScript, services, or Mac OS X which explains the perceived discrepancy between service types. Elucidation is welcome.

Posted on Wednesday, September 12th, 2007.

Tcl:: Language Module

TextWrangler includes basic support for Tcl syntax highlighting and procedure recognition. Procedures are listed in a popup menu that makes it easy to navigate around large files. Unfortunately, this mechanism does not recognize procedures defined in a namespace with the concise namespace::procedure syntax. The intervening “::” characters are not among those TextWrangler normally recognizes as valid identifier characters.

For example, “Bar” appears in the function menu when a procedure is defined like this:

namespace eval Foo {
    proc Bar {} {
        puts "Hello, World!"
    }
}

I prefer this syntax:

proc Foo::Bar {} {
    puts "Hello, World!"
}

but procedures defined this way don’t appear in the function menu:

TextWrangler's Default Tcl Procedure Recognition

Rather than forfeit the convenience of the function popup or conform to a more cumbersome convention, I created a codeless language module that essentially duplicates TextWrangler’s built-in Tcl support. The only distinction is that I’ve added the colon to the “Identifier and Keyword Characters” string. As a result, TextWrangler recognizes strings containing colons as valid procedure names:

Namespace Compatible Tcl Procedure Recognition

Unfortunately, this approach appears to prevent recognition of the nested syntax. Ideally I would like to support both styles.

Download

I believe this module should be compatible with BBEdit as well as TextWrangler.

Tcl:: Codeless Language Module 1k .zip

Installation

Put Tcl.plist in ~/Library/Application Support/TextWrangler/Language Modules/ and restart TextWrangler. The module identifies itself as “Tcl::” to differentiate it from TextWrangler’s internal “Tcl” mode, which remains available. It may be necessary to tweak your language preferences to use this module by default, as shown here.

Please share any problems you encounter with this module, particularly those related to the colon’s promoted status.

Posted on Thursday, September 6th, 2007.

Tcl starting points for ThisService

Waffle Software has released ThisService 2.0, a utility that bundles simple scripts as services accessible throughout Mac OS X. Jesper has included example scripts written in a variety of popular languages to help service authors get started. I like Tcl (have you met?), so I have prepared similar templates for Tcl programmers.

Download Tcl service starting points 2.3K

Tcl supports Unicode intrinsically, so, as with the other starting points, these templates should handle whatever text you throw at them without complaint.

Posted on Thursday, September 6th, 2007.

Linking to an informal group of Flickr photos associated by date

I’ve wanted a way to link to batches of Flickr photos associated by date without grouping them explicitly. There are probably cleaner ways to do this, but Flickr’s search mechanism can be used for this purpose, as demonstrated in a couple of my recent posts.

Start with http://flickr.com/search/ and add the following criteria as parameters:

Whose Pictures?

Restrict search results to a particular user ID (such as yours) using the w parameter. You can look up user IDs easily using idGettr. If present in the user ID, the “@” symbol should be escaped as %40.

w=93931947%40N00

Of What?

Define a search query with q and specify what should be searched with m. The possible values for m are text and tags. In my case, I’m just looking for photos tagged as drawings.

q=drawing
m=tags

From When?

Use the d parameter to define a date range to search. Only photos that fall within this range which match the query defined above will be returned. The date range is given in three parts delimited by hyphens.

First, either posted or taken is used to specify whether you’re talking about the date the photos were posted to Flickr or actually taken. Then the starting date is given, followed by the ending date.

Dates are given as eight-digit strings in the format YYYYMMDD (year, month, day, padded with zeros if necessary). To search a single day, give the same date for the start and finish, as I have:

d=posted-20070202-20070202

Display Mode

If the z parameter is given the value t, the results will be displayed as a grid of thumbnail images. If omitted, matching photos will be presented in a less compact annotated list format.

z=t

Putting it All Together

Separate each parameter=value pair with an ampersand (“&”) and use a question mark to indicate where the parameters begin. The final URL will look something like this:

http://flickr.com/search/?w=93931947%40N00&q=drawing&m=tags&d=posted-20070202-20070202&z=t

As I said, it’s quite likely there’s already an easier way to do this. If not, I suppose I could write a little script to do the trick.

Posted on Saturday, February 3rd, 2007.