Version 0 of Actions

Updated 2004-05-21 03:24:14

Bryan Oakley - 20-May-2004

An action is a pseudo-object that has properties that mostly resemble procs. In addition, actions have state and are generally associated with one or more widgets.

Bryan Schofield posted to comp.lang.tcl a nice example of one implementation of actions [L1 ]. The use of actions is also mentioned at Tk coding styles and philosophies.

Following is a quick example of how actions can be used, using a simpler implementation than the one described by Bryan Schofield. This comes from what I presently use and which, I might add, takes only 177 lines of code to implement (and 20% of that is comments).

    button .cut -text Cut -command [list action invoke cutAction]
    .menubar.editMenu add command -label "Cut" -command [list action invoke cutAction]
    .popup add command -label "Cut" -command [list action invoke cutAction]
    action associcate cutAction .cut [list .menubar.editMenu Cut]
    bind all <Control-x> [list action invoke cutAction]
    ...
    action define cutAction {} {
        <code to cut the selected text>
    }
    proc toggleSelection {} {
        # called whenever the selection changes
        if {<there is a selection>} {
            action disable cutAction copyAction
        } else {
            action enable cutAction copyAction
    }

Notice that the use of actions adds only one extra line of code in your application, and is actually a net loss of several lines of code. Without actions you might have to code toggleSelection as:

    proc toggleSelection {} {
        if {<there is a selection>} {
            .cut configure -state normal
            .menubar.editMenu entryconfigure "Cut" -state normal
            .popup entryconfigure "Cut" -state normal
        } else {
            ...
        }

Not only that, but without actions you also have to add code to make sure that if there is no selection that the procedure that does the cutting won't do anything if called via the accelerator.

As you can see, actions make it much easier to maintain a consistent state across all widgets that implement a particular functionality. You only have to make a single function call to disable all controls that invoke the particular action. In addition, should you add new controls (speech recognition, gestures, additional buttons or menus, etc) you do not have to hunt down all the places where you might need to enable the widgets; associate the new control with the action and the action takes care of the rest.

The above is just one sort of implementation. Many people might prefer a more object oriented approach. For example:

    new Action cutAction {...}
    cutAction button .cut -label "Cut" ...
    cutAction menuitem .menubar.editMenu "Cut" ...
    cutAction menuitem .popup "Cut" ...
    cutAction accelerator <Control-x> .

Making your applications scriptable

In addition to the sheer convenience of actions, they also provide a nice API into your application, making your scripts scriptable. The scripts can be used to implement user defined macros, automated tests, or even compound actions.