TweetFollow Us on Twitter

A platform for protecting mail servers.

Volume Number: 20 (2004)
Issue Number: 6
Column Tag: Programming

QuickTime Toolkit

by Tim Monroe

Tickle Me

Developing QuickTime Applications with Tcl/Tk

Introduction

Tcl/Tk is a combination of two distinct elements: Tcl, which is a string-oriented scripting language and runtime library, and Tk, which is a toolkit for constructing graphical user interfaces. These elements were designed and implemented more or less concurrently at U.C. Berkeley beginning in 1988 by a team led by John Ousterhout. His goal was to develop an extensible language that various software tools could use to control the execution and processing of commands. Indeed, "Tcl" is an acronym for Tool Command Language (and is usually pronounced "tickle").

One distinctive feature of Tcl/Tk -- at least compared with the development environments we've considered in the past dozen articles -- is that its source code is publicly available and largely free of restrictions on its use. This has several important consequences. First, it means that Tcl can easily be embedded into an existing application to provide a scripting interface for that application; if necessary, Tk can also be embedded into that application to provide a graphical user interface. Second, it means that new capabilities can easily be added to Tcl and Tk by defining new commands and GUI objects. For present purposes, we'll be interested in a Tcl/Tk add-on called QuickTimeTcl, which allows us to work with QuickTime movies in a Tcl/Tk application. QuickTimeTcl is also freely available and can be incorporated into commercial and non-commercial products.

In this article and the next, I want to take a look at using Tcl/Tk and QuickTimeTcl to develop QuickTime applications. We'll construct an application, called TickLeez, that can open, display, edit, and play one or more QuickTime movies, while permitting all the standard sorts of user interaction. In this first article, we'll begin with an overview look at Tcl, Tk, and QuickTimeTcl. Then we'll see how to start up a Tcl/Tk application, how to construct and handle our application's menus, how to display dialog boxes, and how to open a QuickTime movie in a window. In the next article, we'll continue our investigation by seeing how to handle movie editing and document-related tasks.

All three important elements -- Tcl, Tk, and QuickTimeTcl -- are available for both Macintosh and Windows operating systems. We'll write our application script so that it is aware of the operating environment and behaves appropriately on each platform. As we saw in our previous articles on QuickTime for Java (in MacTech, January, February, and March 2004), this mainly involves making sure that the menus are arranged correctly (for instance, putting the Quit menu item in the File menu on Windows and in the Application menu on Mac OS X). There are also several other minor issues that require different treatment on each operating system. On the whole, however, we'll see that Tk, like Java's AWT and Swing, provides a very usable platform-independent graphical toolkit that allows us to script user interface objects with little or no concern as to whether they are being displayed on Mac or Windows computers.

Tcl Overview

As already noted, Tcl is a scripting language used by tools for processing commands. Tcl is refreshingly simple and readable, particularly when compared with other popular scripting languages (for instance, Perl). It's a procedural language that has a predominately C-like syntax. Tcl does not provide any object-oriented primitives, but there is an object-oriented extension of Tcl, called "[incr tcl]". (You'll learn shortly what the name signifies.) In these articles, we'll be content to work with standard Tcl.

Getting Started

A Tcl script is a series of one or more commands, each of which has this structure:

command arguments

The number of arguments is determined by the specific command. For instance, we can print the phrase "hello, world" with this line of script:

puts stdout "hello, world"

Here "puts" is the command, and "stdout" and "hello, world" are the two arguments for the command. Notice that the second argument is the entire 12-character sting enclosed by the quotation marks ("").

We can assign a value to a variable using the set command, and we can read a variable's current value by prefixing the $ character. For instance:

set message "hello, world"
puts stdout $message

These two lines produce exactly the same output as the single line shown above.

We can prevent substitution of variable values by prefixing the $ character with a backslash:

puts stdout \$message

This will produce the output "$message". Alternatively, we can use braces to delimit arguments whose variables are not to be substituted:

puts stdout {$message}

Once again, this will produce the output "$message".

Braces can also be used to delimit the names of variables when the variable name is embedded within a longer string. For instance:

puts stdout ${message}ling

This will produce the output "hello, worldling".

Evaluating Commands

A command occurring by itself on a line is evaluated and any value the command produces is returned as the command result. It's also possible to nest commands on a single line, using brackets. Here's an example:

   set x [expr [winfo screenwidth .myWindow]/2 - 180]

Here we call two nested commands, winfo (a Tk command that retrieves the specified information about a window, in this case the width of the screen that contains the window .myWindow) and expr (a Tcl command that evaluates the expression passed to it). Once this command line is executed, the variable x will contain the horizontal position at which we should position a window that is 360 pixels wide if we want to center it on the screen.

Tcl provides a large number of built-in commands. We've already encountered the set, puts, and expr commands. A handful of additional Tcl commands are unset, if, while, switch, array, and return. The incr command increments the value of a variable by a specified amount, or by 1 if no amount is specified. Thus, if the value of the variable xyz is 10, then the following command will set the value of xyz to 11:

incr xyz

In Tcl, any built-in commands can be redefined. In the following article, we'll need to redefine the built-in command exit to allow TickLeez a chance to clean up before the application terminates.

Defining Procedures

We can define procedures in Tcl using the proc command. The general form of a procedure definition is this:

proc name {arguments} {commands}

For instance, Listing 1 defines two procedures, min and max, which return the smaller or larger of the two values passed in as arguments.

Listing 1: Getting greater and lesser values

max, min
proc max {a b} {
   if {$a > $b} {set a} else {set b}
}
proc min {a b} {
   if {$a < $b} {set a} else {set b}
}

Notice that the argument list is enclosed in braces, to delay evaluation of the arguments until the procedure is called. Notice also that a value is returned to the caller as the side effect of executing the set command. (A procedure's return value is the return value of the last command executed in the procedure.) We could just as easily write the min procedure like this, which uses the return command:

proc min {a b} {
   if {$a < $b} {return $a} else {return $b}
}

There is one final item to keep in mind: there must be a space following the right brace that encloses the argument list; otherwise the Tcl interpreter will complain that there are extra characters after the closing brace.

Using Structured Data

The basic data type in Tcl is the string; all arguments are passed as strings, although certain commands (for instance incr) may interpret their input numerically. Tcl also provides two ways to combine strings into structures: lists and arrays. A list is roughly equivalent to a string whose elements are separated from one another by white space. Tcl provides a large number of commands to create and operate on lists, including list, lindex, llength, lappend, concat, and split. List indices start at 0; the word end can be used to pick out the last element in a list.

For large collections of data elements, it's often easier to use an array, which is a variable whose values are addressed using a string index. (Tcl arrays are also called associative arrays, dictionaries, or hashes.) In TickLeez, we will employ arrays extensively. In particular, we will store six pieces of global application data in an array called appData. The array element appData(winNo) will contain the number of the next window to open; we'll use this information to construct a unique internal name that identifies that window. The array element appData(nextNewWinNo) will contain the number of the next new window to open; we'll use this information to construct a movie name of the form "Untitledn.mov". The array element appData(os) will contain a string that identifies the current operating system. The array element appData(modKey) will contain the name of the OS-specific modifier key (namely, either "Command" or "Control"). The array element appData(winTag) will contain a unique tag we use to construct the names of our movie windows. Finally, the array element appData(tempDir) will contain the full pathname of a directory that holds any temporary files we need to create.

We'll also need to associate some data with each movie window that we open. For instance, we'll need to maintain information about the movie contained in a window, such as its edited state or the pathname of the file it was loaded from. Unfortunately, Tk does not provide a standard means to attach a list or array to a specific window (as we could do using the functions SetWRefCon and SetWindowLong in our C-based applications). We can work around this limitation by storing that data in the appData global associative array using array indices that contain the window's name. For instance, if the winName variable contains the name of a specific window, then we can use the string "$winName,fileName" as an index into the appData array; the value associated with that index is the full pathname of the movie file.

We'll use this little trick to store five pieces of information in the appData array for each open movie. The array element appData($winName,movie) will contain the QuickTimeTcl identifier for the movie. The array element appData($winName,dirty) will contain the character "1" or "0", indicating whether the movie has been edited or not. The array element appData($winName,undoLevel) will contain the undo level for the movie (that is, the number of undoable edits that have been applied to the movie). Finally, the array element appData($winName,undoOp,n) will contain a string indicating which edit operation was applied a the undo level n. We'll use that string to set the text of the Undo menu item, as we'll see in the next article.

In order for the elements of the appData associative array to be visible throughout our application, we need to include the following line at the beginning of any procedure that accesses the data in that array:

global appData

Note that we could have used individual variable names for the six pieces of global application data; by collecting them all into one array, we reduce the number of global statements we need to use in each procedure.

Tk Overview

Tk (short for "Toolkit" and usually pronounced "tee-kay") is a toolkit for constructing graphical user interfaces. We can use it to create and display the windows, alert boxes, dialog boxes, menus, and other visible elements of our application. Moreover, Tk handles events and user actions involving the elements in those windows, dialog boxes, and menus. In other words, it provides event-driven control flow for our applications. Originally Tk was designed to work in conjunction with the X Windowing System, but subsequently it has been modified to support other graphical user interface systems, including those on Mac OS and Windows. On Mac OS X, Tk uses the Aqua interface, although it currently has a few drawing and event-handling glitches.

Creating Windows

In Tk parlance, a widget is any visible user interface element. In TickLeez, we'll be mainly concerned with three kinds of widgets: windows, menus, and movies. It's very easy to create a new window. Here's part of the code we'll use to create our application's About box:

toplevel .about -width 360 -height 160

The toplevel command creates a new toplevel window, in this case one that is 360 by 160 pixels. The first argument to the toplevel command is the name of the new window. In Tk, widgets are arranged in a hierarchy, the root of which is a toplevel window whose name is ".". All new widgets must have names that are relative to some existing widget. In particular, all widgets must begin with the "." character. As you can see, the name of our About box is ".about".

The names of windows are reasonably arbitrary, as long as they too begin with the "." character. To make it easy to find all and only the movie windows, let's give all our movie windows names of the form ".winRTMn", for some integer n. So the first movie window we create will have the name ".winRTM1"; the second will be ".winRTM2", and so on. As noted earlier, the array element appData(winNo) holds the next available window number, so we can generate a movie window name like this:

set winName $appData(winTag)[incr appData(winNo)]

Based on this naming scheme, we can tell whether a given window is a movie window by passing its name to the isMovieWindow procedure, shown in Listing 2. isMovieWindow just looks to see whether the winName argument begins with the string "winRTM".

Listing 2: Finding movie windows

isMovieWindow
proc isMovieWindow {winName} {
   global appData
   if {[string first $appData(winTag) $winName] == 0} {
      return 1
   } else {
      return 0
   }
}

An alternative strategy is to set a movie window's class by including the "-class RTM" option when we create the movie window. In that case, the if line in Listing 2 would look like this:

   if {[string equal [winfo class .$winName] RTM]} {

In a large application with many kinds of windows, using window classes is probably preferable. In TickLeez, since we display only movie windows and the About box, we'll stick with the method that uses a set of special window names.

We can find the frontmost movie window by calling the topMovieWindow procedure defined in Listing 3. First we create a list of all open toplevel windows by executing the wm stackorder command. Then we look at each window, from top to bottom, until we find one that satisfies the isMovieWindow procedure.

Listing 3: Finding the frontmost movie window

topMovieWindow
proc topMovieWindow {} {
   global appData
   set winlist [wm stackorder .]
   set index [expr [llength $winlist] - 1]
   
   while {![isMovieWindow [string range \
      [lindex $winlist $index] 1 end]] && ($index >= 0)} {
      incr index -1
   }
   return [string range [lindex $winlist $index] 1 end]
}

The calls to range chop off the first character of the specified list element (that is, "."). If no movie windows are open, topMovieWindow returns an empty string.

Creating Menu Bars and Menus

We can use Tk to create and manage menu bars and menus, which can contain standard menu items, separator lines, checkbutton items, and radiobutton items. A checkbutton menu item toggles between two states, and radiobutton menu items are organized into groups such that exactly one item in the group is selected at any time. Menu items are in fact just buttons that behave in specific ways and that are grouped into menus.

A menu bar is always associated with a specific toplevel window; this means that we'll need to create a menu bar and a set of menus for each movie window opened by TickLeez. On Windows computers, the menu bar is contained in the movie window itself, as shown in Figure 1. On Macintosh computers, the menu bar is drawn at the top of the main screen, as usual.


Figure 1: A movie window displayed by TickLeez (Windows)

A menu bar is in fact itself a menu. In Tk this is known as a cascading menu, since the items of the menu cascade from left to right. We create a menu bar by executing the menu command:

set bar [menu ${w}mbar -tearoff 0]

Here the variable w is assumed to be the full name of a window. So, for instance, the movie window whose name is ".winRTM1" would have an associated menu bar whose name is ".winRTM1mbar". We associate a menu bar with a window like this:

$w configure -menu $bar

On Macintosh computers, we will create a menu bar and set of menus for the root window ".", even though we hide the root window during application startup. This allows TickLeez to display a menu bar even when no movie windows are open. On Windows, we don't create a menu bar and set of menus for the root window; instead, we create a new empty movie window (Figure 2) when the application starts up; the user can use the menus in that window to open movie files. (We'll see the code that does all this a bit later, in Listing 7.)


Figure 2: A new movie window displayed by TickLeez (Windows)

We can add menus to the menu bar with the add cascade command. Here's how we'll create a File menu and add it to the menu bar:

set m [menu $bar.file -tearoff 0]
$bar add cascade -label "File" -menu $m

And we can add menu items to a menu with the menu's add command command. Here's how we'll add the New item to the File menu:

$m add command -label "New" \
      -accelerator "$appData(modKey)-N" -command {newDoc}   

Notice that we specify the TickLeez procedure that is to be called when this menu item is selected by using the -command argument.

Listing 4 show the complete definition of the setupMenus procedure.

Listing 4: Setting up the menus and menu items

setupMenus
proc setupMenus {w} {
   global appData
   # create a new menu bar
   set bar [menu ${w}mbar -tearoff 0]
   
   # File menu   
   set m [menu $bar.file -tearoff 0]
   $bar add cascade -label "File" -menu $m
   
   $m add command -label "New" \
         -accelerator "$appData(modKey)-N" -command {newDoc}   
   $m add command -label "Open..." \
         -accelerator "$appData(modKey)-O" -command {openDoc}
   $m add command -label "Close" \
         -accelerator "$appData(modKey)-W" -command {closeDoc}
   
   $m add sep
   
   $m add command -label "Save" \
         -accelerator "$appData(modKey)-S" -command {saveDoc}
   $m add command -label "Save As..." \
         -accelerator "$appData(modKey)-Shift-S" \
         -command {saveAsDoc}
   
   if {[string equal $appData(os) "windows"]} {
      $m add sep
      $m add command -label "Exit" \
         -accelerator "$appData(modKey)-Q" -command {exit}
   }
   
   #   Edit menu
   set m [menu $bar.edit -tearoff 0]
   $bar add cascade -label "Edit" -menu $m
   
   $m add command -label "Undo" \
         -accelerator "$appData(modKey)-Z" -command {undoDoc}
   
   $m add sep
   
   $m add command -label "Cut" \
         -accelerator "$appData(modKey)-X" \
         -command {editDoc cut}
   $m add command -label "Copy" \
         -accelerator "$appData(modKey)-C" \
         -command {editDoc copy}
   $m add command -label "Paste" \
         -accelerator "$appData(modKey)-V" \
         -command {editDoc paste}
   $m add command -label "Clear" -command {editDoc clear}
   
   $m add sep
   
   $m add command -label "Select All" \
         -accelerator "$appData(modKey)-A" \
         -command {selectDoc all}
   $m add command -label "Select None" \
         -accelerator "$appData(modKey)-B" \
         -command {selectDoc none}
   
   #   Movie menu
   set m [menu $bar.movie -tearoff 0]
   $bar add cascade -label "Movie" -menu $m
   
   $m add command -label "Hide Controller Bar" \
         -accelerator "$appData(modKey)-1" \
         -command {puts "TO BE PROVIDED"}
   
   #   Help menu
   if {[string equal $appData(os) "windows"]} {
      set m [menu $bar.help -tearoff 0]
      $bar add cascade -label "Help" -menu $m
   } else {
      set m [menu $bar.apple -tearoff 0]
      $bar add cascade -menu $m 
   }
   
   $m add command -label "About TickLeez" \
         -command {showAboutBox}
   $w configure -menu $bar
}

We'll encounter the various procedures attached as commands later on in this article and the next.

Adding the About Box

Let's look at a real-life example of using Tk to display and manage a window on the screen, in this case our application's About box. The Windows version of the About box is shown in Figure 3, and the Macintosh version is shown in Figure 4.


Figure 3: The About box of TickLeez (Windows)


Figure 4: The About box of TickLeez (Macintosh)

You'll notice that the appearance of the Macintosh About box isn't quite right; Tk currently is unable to draw the horizontal stripes that form the background of an Aqua dialog box.

The first time the user selects the About TickLeez menu item to display the About box, we'll use the toplevel command to create a new toplevel window, as mentioned above. When the user closes the About box, however, we won't destroy the window. Rather, we'll simply hide the window (by issuing the withdraw command); if the user later decides to display the About box again, we can just show the hidden window with these commands:

wm state .about normal
raise .about

This saves us from having to create and delete the images in the About box each time it's displayed.

Our About box contains four widgets: the OK button, a static text field, and two additional buttons. We can create and position the OK button with this code:

place [button .about.ok -text OK -default active \ 
            -width 5 -command {wm withdraw .about}] \
            -x 284 -y 124

As you can see, the command associated with the OK button is to withdraw (that is, hide) the window .about, which is the About box itself.

The two additional buttons are used to display the images in the About box. In TickLeez, the About box is configured in such a way that clicking the penguin image changes that picture to the Tcl logo. Figure 5 shows the result of clicking the penguin image in Figure 4.


Figure 5: The About box of TickLeez (clicked)

We can load the penguin image like this:

set penguin [image create photo -file \
      [file join [pwd] penguin.jpeg]]

And we create and position the button like this:

place [button .about.pen -image $penguin \
      -width 76 -height 105 -relief flat \
      -command "raise .about.tcl .about.pen"] -x 20 -y 8

Toggling the two images is accomplished using the raise command; when passed two arguments, it causes the first image to be drawn on top of the second image. Since our two images are cleverly configured to be the same size, this causes one image to become visible and the other invisible.

Listing 5 shows our complete definition of the showAboutBox procedure.

Listing 5: Showing the About box

showAboutBox
proc showAboutBox {} {
   global appData
   # if the About box exists already, just show it and bring it to the front
   if {[winfo exists .about]} {
      wm state .about normal
      raise .about
      return
   }
   # otherwise, create the About box
   toplevel .about -width 360 -height 160
   
   wm resizable .about 0 0
   wm title .about "About TickLeez"
   
   if {[string match "mac*" $appData(os)]} {
      ::tk::unsupported::MacWindowStyle style .about \
         document {closeBox}
   }
   place [button .about.ok -text OK -default active \ 
            -width 5 -command {wm withdraw .about}] \
            -x 284 -y 124
   place [label .about.what -wraplength 220 -justify left \
            -text "A QuickTime movie player built with \
            Tcl/Tk.\n\n\u00a9 2004 by Tim Monroe"] -x 120 -y 12
   set penguin [image create photo -file \
      [file join [pwd] penguin.jpeg]]
   set feather [image create photo -file \
      [file join [pwd] tcl.jpeg]]
   place [button .about.pen -image $penguin \
            -width 76 -height 105 -relief flat \
            -command "raise .about.tcl .about.pen"] -x 20 -y 8
   place [button .about.tcl -image $feather \
            -width 76 -height 105 -relief flat \
            -command "raise .about.pen .about.tcl"] -x 20 -y 8
   raise .about.pen .about.tcl
   bind .about <Return> {.about.ok flash; .about.ok invoke}
   wm protocol .about WM_DELETE_WINDOW {.about.ok invoke}
   
   # center the About box on the screen
   set x [expr [winfo screenwidth .about]/2 - 180]
   set y [expr [winfo screenheight .about]/2 - 80]
   wm geom .about +$x+$y
}

Notice that Return key presses and clicks in the window's close box are configured to have the same effect as pressing the OK button.

We have laid out the widgets in this dialog box using the place command, by specifying item positions in terms of absolute coordinates in the dialog box. Here we are using the place geometry manager. Tk also provides the pack geometry manager, which lays out items in frames, which can be grouped and positioned relative to other frames. For complex widget positioning, the pack geometry manager is virtually always preferred. Indeed, using the place geometry manager with explicit coordinates is generally considered bad practice. We can get away with using it here because our About box is so simple.

QuickTimeTcl Overview

QuickTimeTcl is an extension to Tcl/Tk that we can use to open, display, and modify QuickTime movies. It was originally written by Bruce O'Neel, and has subsequently been significantly enhanced and extended by Mats Bengtsson. QuickTimeTcl supports QuickTime movie playback and editing on all three major platforms currently supported by QuickTime: classic Mac OS 8 and 9, Mac OS X, and Windows.

QuickTimeTcl extends Tcl/Tk by defining two new widgets, movie and seqgrabber, which support movie playback and editing, and audiovisual capture. In addition, QuickTimeTcl supports a handful of other commands that provide information or supplementary capabilities; for instance, it supports the function tk_getOpenFilePreview, which extends the Tk function tk_getOpenFile by adding a file preview to the file-opening dialog box and by automatically restricting the selectable files to those that can be opened by QuickTime. Here's how easy it is to open a file using QuickTimeTcl:

package require QuickTimeTcl 
set filename [tk_getOpenFilePreview]
movie .m -file $filename
pack .m

We indicate that the QuickTimeTcl package is required; we elicit a filename from the user; we configure a movie object in the root window; and then we pack the movie widget into the root window. Since the movie is the only widget in the window, the window is sized to exactly hold the movie.

QuickTimeTcl is a reasonably complete Tcl/Tk wrapper for the most common QuickTime APIs. It provides support for movie editing, importing and exporting, applying video effects to movies and images, displaying movies full screen, and creating movies from a series of images. QuickTimeTcl also provides a mechanism to intercept and monitor a large number of messages passed to the QuickTime movie controller, including notifications of key events, custom button clicks, time and volume changes, mouse button clicks, selection changes, and (for QuickTime VR movies) changes in the pan, tilt, or field of view angles. This mechanism stands in for several of QuickTime's callback procedures, in particular for the movie controller action filter procedure.

If we need to extend QuickTimeTcl to support capabilities that it does not currently provide (for instance, support for intermovie communication), we can download and modify the source code, which is freely available. The QuickTimeTcl extension is written in fairly vanilla C, and indeed relies in part on some of the sample code we've considered earlier in this QuickTime Toolkit series of articles. For instance, Listing 6 shows the routine that QuickTimeTcl uses to determine the height of the movie controller bar.

Listing 6: Finding the height of the controller bar

GetControllerBarHeight
short GetControllerBarHeight (MovieController mc) 
{
   Boolean         wasAttached = false;
   Rect               myRect;
   short            myHeight = 0;
   
   /* If the controller bar is attached, detach it (and remember we did so) */
   if (MCIsControllerAttached(mc) == 1) {
      wasAttached = true;
      MCSetControllerAttached(mc, false);
   }
   
   /* Get the rectangle of the controller */
   MCGetControllerBoundsRect(mc, &myRect);
   myHeight = myRect.bottom - myRect.top;
   
   /* Now reattach the controller bar, if it was originally attached */
   if (wasAttached) {
      MCSetControllerAttached(mc, true);
   }
   return(myHeight);
}

If you have a very good memory, you may recognize this as a slightly modified version of the function QTUtils_GetControllerBarHeight that we encountered in the article "Movie Controller Potpourri" (MacTech, February 2000).

Note that QuickTimeTcl can also be used as a pure scripting tool, with no user interface. This would allow us, for instance, to use Tcl scripts to automate various movie operations, such as creating a movie from a series of images or transcoding existing movies into new formats. In these articles, we will not investigate this way of using QuickTimeTcl.

Application Startup

Let's get started writing TickLeez, which we want to be able to open and display QuickTime movies, allow the user to edit those movies, and exhibit all the standard document-related behaviors. The TickLeez script begins with the "package require QuickTimeTcl" directive considered earlier. Then it defines several dozen procedures and ends with this command:

main

Our main procedure performs launch-time initializations and adds the six pieces of global information to the appData array. Listing 7 shows our implementation of the main procedure. We make sure that the Tk package is available and then set the appData(os) array element to either "windows", or "macintosh", or "macosx", depending on whether the script is executing on Windows, Mac OS 8 or 9, or Mac OS X.

Listing 7: Opening the application

main
proc main {} {
   global appData
   
   # make sure the resources we need are available
   if {[catch {package require Tk}]} {
        tk_messageBox -icon error -type ok \
                           -message {Tk did not initialize.}
      exit 1
   }
   # we use a variable 'appData(os)' that is more convenient for Mac OS X
   switch -- $::tcl_platform(platform) {
      unix {
         set appData(os) $::tcl_platform(platform)
         if {[package vcompare [info tclversion] 8.3] == 1} {   
            if {[string equal [tk windowingsystem] "aqua"]} {
               set appData(os) "macosx"
            }
         }
      }
      windows - macintosh {
         set appData(os) $::tcl_platform(platform)
      }
   }
   
   if {[string match "mac*" $appData(os)]}  {
      set appData(modKey) Command
   } else {
      set appData(modKey) Control
   }
   
   # initialize global variables
   set appData(winNo) 0
   set appData(nextNewWinNo) 0
   set appData(winTag) winRTM
   # create a new empty directory for temporary files
   if {[info exists ::env(TMPDIR)] && \
         [file isdirectory $::env(TMPDIR)]} {
      set appData(tempDir) \
            [file join $::env(TMPDIR) $appData(winTag)[pid]]
  } else {
      if {[string match "mac*" $appData(os)]} {
         set tmpDir /tmp
      } else {
         set tmpDir [pwd]
      }   
      
      set appData(tempDir) \
            [file join $tmpDir $appData(winTag)[pid]]
   }
   if {[file exists $appData(tempDir)]} {
      file delete -force $appData(tempDir)
   }
   
   file mkdir $appData(tempDir)
   # set the current directory
   cd [file dirname [info script]]
   # set up the menus
   if {[string match "mac*" $appData(os)]} {
      setupMenus .
      adjustMenus
      setupBindings ""
   } else {
      newDoc
   }
   # hide the root window and the Console window
   wm withdraw .
   console hide
}

If we're running on Windows, we call the newDoc procedure to open a new empty movie window. (See Figure 2 again.) Otherwise, we call the three procedures setupMenus, adjustMenus, and setupBindings, which we'll consider in depth in the next article. On both Mac and Windows, we hide the root window (whose name is ".") and the Console window.

Movie Windows

Earlier we saw how to use the toplevel command to create a window. A movie window in TickLeez is just a toplevel window that contains a movie widget. We can create a movie widget by specifying a unique pathname to the movie command:

movie .$winName.mov

And we can attach a movie file to that widget using the configure command:

.$winName.mov configure -file $fileName

In TickLeez, we want a movie window to be editable and resizable; we also want to be able to intercept movie controller actions targeted at the movie. So we'll call the configure command again, like this:

.$winName.mov configure -mccommand controllerProc \
               -mcedit 1 -resizable 1

In the next article, we'll see how to define the controllerProc procedure.

Next we need to resize the window to exactly contain the movie and its associated movie controller bar. We can use the pack command, which resizes a window to exactly contain the widgets inside it:

pack .$winName.mov

Listing 8 shows the complete definition of the openFileInWindow function. Given a full pathname for a movie file and a value that indicates whether the movie is a new movie, it opens that movie in a window on the screen. (Note that the -resizable option does not appear to work correctly on Windows. This may be fixed in a future QuickTimeTcl release.)

Listing 8: Opening a movie in a new window

openFileInWindow
proc openFileInWindow {fileName isNew} {
   global appData
   # create a unique Tk path for the new window
   set winName $appData(winTag)[incr appData(winNo)]
   
   # create a new window
   toplevel .$winName
   wm resizable .$winName 0 0
   wm title .$winName [file tail $fileName]
   
   wm protocol .$winName WM_DELETE_WINDOW \
               "attemptClose $winName closing"
   if {[string match "mac*" $appData(os)]} {
      ::tk::unsupported::MacWindowStyle style .$winName \
         document {closeBox horizontalZoom collapseBox}
   }
   movie .$winName.mov
   
   if {$isNew} {
      .$winName.mov new $fileName
   } else {
      .$winName.mov configure -file $fileName
   }
   
   .$winName.mov configure -mccommand controllerProc \
               -mcedit 1 -resizable 1
   pack .$winName.mov
   
   # save window-specific info in global array
   set appData($winName,movie) .$winName.mov   
   set appData($winName,dirty) 0   
   set appData($winName,fileName) $fileName   
   set appData($winName,undoLevel) 0
   
   # force the new window to be displayed
   update idletasks
   
   setupMenus .$winName
   adjustMenus
   
   setupBindings $winName
}

The openFileInWindow procedure is called by two menu-handling procedures, newDoc and openDoc. The newDoc procedure, shown in Listing 9, creates a filename for a non-existent file in our application's temporary directory. QuickTimeTcl requires that a file be associated with every local movie it manages, even for new empty movies.

Listing 9: Creating a new empty movie

newDoc
proc newDoc {} {
   global appData
   
   # new windows are entitled "Untitled.mov", "Untitled2.mov", "Untitled3.mov"....
   incr appData(nextNewWinNo)
   set visWinNum $appData(nextNewWinNo)
   if {$visWinNum == 1} {
      set visWinNum ""
   }
   
   # construct a new temp file name
   set filename [file join $appData(tempDir) \
                                       "Untitled${visWinNum}.mov"]
   
   openFileInWindow $filename 1
}

The openDoc function, shown in Listing 10, elicits a filename from the user by calling the tk_getOpenFile command. This command displays the standard file-opening dialog box and returns the full pathname of the selected file (or the empty string if no file was selected).

Listing 10: Handling the Open menu item

openDoc
proc openDoc {} {
   set filename [tk_getOpenFile -title "TickLeez: Open a \
                                                         Movie File"]
   if {$filename != ""} {
      openFileInWindow $filename 0
   }
}

Conclusion

In this article, we've taken a preliminary look at Tcl/Tk and the QuickTimeTcl add-on, which allows us to use the Tcl/Tk scripting language to build a QuickTime-savvy application. We've fashioned our menus and menu bars, and we've seen how to add a simple dialog box -- our application's About box -- to an application. Most important, we learned how to use QuickTimeTcl commands to open a movie file and display it in a movie on the screen.

In the next article, we'll continue working with Tcl/Tk as a delivery platform for QuickTime applications. We'll see how to bind certain events to our application's procedures, and we'll see how to handle standard movie editing. That of course will require that we also implement the standard document behaviors, such as prompting the user to save or discard unsaved changes to a movie that's being closed.

Acknowledgements and References

Special thanks are due to Jim Ingham and to Mats Bengtsson for reviewing a draft of this article and for providing invaluable feedback.

The latest Tcl/Tk release for Windows computers is available at the Tcl/Tk Developer Xchange site (http://www.tcl.tk). You can download the QuickTimeTcl extension at http://hem.fyristorg.com/matben/qt. For Macintosh OS X computers, you should use the all-inclusive Tcl/Tk distribution available at http://sourceforge.net/projects/tcltkaqua/.


Tim Monroe is a member of the QuickTime engineering team at Apple. You can contact him at monroe@mactech.com. The views expressed here are not necessarily shared by his employer.

 
AAPL
$119.00
Apple Inc.
+1.40
MSFT
$47.75
Microsoft Corpora
+0.28
GOOG
$540.37
Google Inc.
-0.71

MacTech Search:
Community Search:

Software Updates via MacUpdate

Skype 7.2.0.412 - Voice-over-internet ph...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
HoudahSpot 3.9.6 - Advanced file search...
HoudahSpot is a powerful file search tool built upon MacOS X Spotlight. Spotlight unleashed Create detailed queries to locate the exact file you need Narrow down searches. Zero in on files Save... Read more
RapidWeaver 6.0.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
iPhoto Library Manager 4.1.10 - Manage m...
iPhoto Library Manager lets you organize your photos into multiple iPhoto libraries. Separate your high school and college photos from your latest summer vacation pictures. Or keep some photo... Read more
iExplorer 3.5.1.9 - View and transfer al...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
OneNote 15.4 - Free digital notebook fro...
OneNote is your very own digital notebook. With OneNote, you can capture that flash of genius, that moment of inspiration, or that list of errands that's too important to forget. Whether you're at... Read more

Latest Forum Discussions

See All

Raby (Games)
Raby 1.0.3 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.3 (iTunes) Description: ***WARNING - Raby runs on: iPhone 5, iPhone 5C, iPhone 5S, iPhone 6, iPhone 6 Plus, iPad Mini Retina, iPad Mini 3, iPad 4, iPad Air,... | Read more »
Oddworld: Stranger's Wrath (Games)
Oddworld: Stranger's Wrath 1.0 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0 (iTunes) Description: ** PLEASE NOTE: Oddworld Stranger's Wrath requires at least an iPhone 4S, iPad 2, iPad Mini or iPod Touch 5th gen... | Read more »
Bounce On Back (Games)
Bounce On Back 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: | Read more »
Make Way for Fat Chicken, from the Maker...
Make Way for Fat Chicken, from the Makers of Scrap Squad Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Relevant Games has announced they will be releasing their reverse tower defense game, | Read more »
Tripnary Review
Tripnary Review By Jennifer Allen on November 26th, 2014 Our Rating: :: TRAVEL BUCKET LISTiPhone App - Designed for the iPhone, compatible with the iPad Want to create a travel bucket list? Tripnary is a fun way to do exactly that... | Read more »
Ossian Studios’ RPG, The Shadow Sun, is...
Ossian Studios’ RPG, The Shadow Sun, is Now Available for $4.99 Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Mmmm, Tasty – Having the Angry Birds for...
The very first Angry Birds debuted on iOS back in 2009. When you sit back and tally up the number of Angry Birds games out there and the impact they’ve had on pop culture as a whole, you just need to ask yourself: “How would the birds taste... | Read more »
Rescue Quest Review
Rescue Quest Review By Jennifer Allen on November 26th, 2014 Our Rating: :: PATH BASED MATCH-3Universal App - Designed for iPhone and iPad Guide a wizard to safety by matching gems. Rescue Quest might not be an entirely original... | Read more »
You Can Play the Final Chapter of Lone W...
You Can Play the Final Chapter of Lone Wolf: Dawn Over V’taag Right Now Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Swords of Anima (Games)
Swords of Anima 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: A new tactical turn-based RPG experience. Command the Savior Rex Squad in an epic journey of courage and deception. Can you... | Read more »

Price Scanner via MacPrices.net

Black Friday: $300 off 15-inch Retina MacBook...
 B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for $300 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina... Read more
2014 1.4GHz Mac mini on sale for $449, save $...
 B&H Photo has the new 1.4GHz Mac mini on sale for $449.99 including free shipping plus NY tax only. Their price is $50 off MSRP, and it’s the lowest price available for this new model. Adorama... Read more
Early Black Friday pricing on 27-inch 5K iMac...
 B&H Photo continues to offer Black Friday sale prices on the 27″ 3.5GHz 5K iMac, in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP... Read more
Early Black Friday sale prices on iPad Air 2,...
 MacMall is discounting iPad Air 2s by up to $75 off MSRP as part of their Black Friday sale. Shipping is free: - 16GB iPad Air WiFi: $459 $40 off - 64GB iPad Air WiFi: $559 $40 off - 128GB iPad Air... Read more
Early Black Friday MacBook Air sale prices, $...
 MacMall has posted early Black Friday MacBook Air sale prices. Save $101 on all models for a limited time: - 11″ 1.4GHz/128GB MacBook Air: $798 - 11″ 1.4GHz/256GB MacBook Air: $998 - 13″ 1.4GHz/... Read more
Why iPhone 6 Tablet/Laptop Cannibalization Is...
247wallst.com blogger Douglas A. McIntyre noted last week that according to research posted on the Applovin blog site the iPhone 6 is outselling the iPhone 6 Plus by a wide margin . Hardly a surprise... Read more
Worldwide Tablet Growth Expected to Slow to 7...
The global tablet market is expected to record massive deceleration in 2014 with year-over-year growth slowing to 7.2%, down from 52.5% in 2013, according to a new forecast from International Data... Read more
Touchscreen Glove Company Announces New Produ...
Surrey, United Kingdom based TouchAbility specializes in design and manufacture of a wide variety of products compatible with touchscreen devices including smartphones, tablets and computers. Their... Read more
OtterBox Alpha Glass Screen Protectors for iP...
To complement the bigger, sharper displays on the latest Apple devices, OtterBox has introduced Alpha Glass screen protectors to the iPhone 6 and iPhone 6 Plus. The fortified glass screen protectors... Read more
Early Black Friday Mac Pro sale, 6-Core 3.5GH...
 B&H Photo has the 6-Core 3.5GHz Mac Pro on sale today for $3499 including free shipping plus NY sales tax. Their price is $500 off MSRP, and it’s the lowest price available for this model from... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.