![]() | ![]() |
|
Scripting Graphical Commands with Tcl/Tk Mini-HOWTOSalvador J. Peralta
One of the richest aspects of Linux is its abundance of commandline utilities. The ability to rapidly provide a graphical frontend for those utilities to make them available to non-technical users can be a handy skill for a developer or administrator to possess. This article provides a cookbook-style tutorial introduction to Tcl and Tk, a scripting language and graphical toolkit that were designed to accomplish that very task.
1. CopyrightCopyright (c) 2003 Salvador Peralta Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. 2. Introduction to Tcl and TkThe tool command language Tcl (pronounced tickle) is an interpreted, action-oriented, string-based, command language. It was created by John Ousterhaut in the late 1980's along with the Tk graphical toolkit. Tcl and the Tk toolkit comprise one of the earliest scripted programming environments for the X Window System. Though it is venerable by today's standards, Tcl/Tk remains a handy tool for developers and administrators who want to rapidly build graphical frontends for command line utilities. Tcl and Tk come bundled with most major Linux distributions and source-based releases are available from tcl.sourceforge.net. If Tcl and Tk are not installed on your system, the source releases are available from the SourceForge Tcl project: http://tcl.sourceforge.net/. Binary builds for most Linux distributions are available from rpmfind.net. A binary release is also available for Linux and other platforms from Active State at http://aspn.activestate.com/ASPN/Tcl. 3. Tcl and Tk BasicsTcl is built up from commands which act on data, and which accept a number of options which specify how each command is executed. Each command consists of the name of the command followed by one or more words separated by whitespace. Because Tcl is interpreted, it can be run interactively through its shell command, tclsh, or non-interactively as a script. When Tcl is run interactively, the system responds to each command that is entered as illustrated in the following example. You can experiment with tclsh by simply opening a terminal and entering the command tclsh.
The previous example illustrates several aspects of the Tcl language. The first line, set a 35 assigns 35 to the variable a using the set command. The second line evaluates the result of 35 times the value of a using the expr command. Note that Tcl, like Perl and Bash requires the use of the dollar operator to get the value of a variable. The open brackets around the expression [ expr 35 * $a ] instruct the interpreter to perform a command substitution on the expression, adds it to the rest of the string and uses the puts command to print the string to Tcl's default output channel, standard output. Tcl's windowing shell, Wish, is an interpreter that reads commands from standard input or from file, and interprets them using the Tcl language, and builds graphical components from the Tk toolkit. Like the tclsh, it can be run interactively. To invoke Wish interactively, start X on your system, open a terminal, and type wish at the command prompt. If your environment is set up properly, this will launch an empty root window and start the windowing shell in your terminal. The following example is a two-line script that is one of the simplest programs that can be created with wish:
Let's break down these two lines of code: button .submit -text "Click Me" -command { puts "\nHello World" }: The button command enables you to create and manipulate the Tk button widget. As with all Tk widgets, the syntax is button .name [-option value] [-option value] .... The curly braces surrounding the puts command allow you to nest the text string, "Hello World", inside of the command without performing any variable substitutions. Other basic widgets include the following: label, checkbutton, radiobutton, command, separator, entry, and frame. Click the button a few times to verify that it works. pack .submit The pack command tells the Tk packer geometry manager to pack the window name as a slave of the master window . which is always referred to by the character .. As with the other Tk widget commands we will see, the syntax is pack .name [-option value] [-option value]. While the previous example was very simple, more advanced examples are nearly as easy to build. Have a look at the following script which creates a simple graphical front end for apachectl ( please note, this example is intended to be run as a script rather than interactively from the shell. You will need to set the permissions of the script as executable and run this script as a user with privileges to start and stop apache ):
This script introduces a few new concepts. Let's look at some of them line by line:
As we saw earlier, the set command is used to assign a value to a variable. As with the previous examples, the syntax is simple: set variable_name value. In order to make the variable available to the Tcl procedures that we are creating in this program, we need to import the apachectl variable into each procedure. This is accomplished using the global command which adds a named variable to the local namespace of a given procedure. The global command accepts one or more variables as arguments and assigns the named variables to each procedure used in the program. Global is also used to export variables that are declared within a procedure's local namespace.
Procedures in Tcl are created with the proc command. The proc command takes the following form: proc name {args} {body} where name is the name of the procedure. Args are the formal arguments accepted by the procedure, and body is the main code of the procedure. Procedures are executed the same way that any other command is executed in Tcl. The script we are currently working with consists of 4 procedures. The first 3 ( start, stop, restart ), simply import the apachectl variable into the local namespace and execute the basic apachectl commands as background processes while the 4th procedure, "screen", uses the packer to build the basic screen and call each of the functions. Let's have a closer look at the screen procedure:
The screen procedure begins by using the frame command to construct the basic frame that will contain the buttons specified further down in the procedure. As this example illustrates, slave widgets are specified by prepending them with the name of their master followed by a ".". The master must already be packed before the slave can use them, so we pack the frame .top before specifying the button command and tell it to fill along the x axis. Last, we use the button command to create 3 buttons as slaves to .top, passing in the appropriate procedure to execute when the button is pressed, and adding a text label using the -command and -text arguments, respectively. 4. Adding FeaturesProviding multiple buttons to control a single application is, perhaps, a bit of overkill, as is calling separate procedures for each action. A third problem is that apachectl prints a message to standard output to indicate how the command has been acted upon. The application could be improved by including a text widget to display the output of apachectl. In the following script, we will redesign the application to use a radiobutton chooser and a single button by modifying the screen procedure , and build a text widget in a new frame. We also remove the start, stop, and restart procedures and create 2 new procedures. The first, init, will handle the conditionals created by the radio button selection, the second, put_text, will launch Apache and print the apachectl output to a text widget:
First, let's have a look at the screen procedure. The radiobutton command works just like html radiobuttons. The -variable parameter accepts the name of the variable as an argument. The -value parameter accepts the variable's value as an argument. The button, .top.submit uses the -command parameter to to call the init procedure defined later in the script. These buttons are then packed into the top frame and a second frame called bottom is created. The bottom frame is composed of a text widget and a scrollbar. Text widgets are created with the text command which takes a variety of options. In this case, we have used the -relief option which specifies the 3D effect for the field (other values for -relief include raised, flat, ridge, solid, groove); -bd option, which specifies borderwidth; and the yscrollcommand which specifies the name of a scrollbar that will be engaged by the textfield. Our scrollbar widget takes one option, -command which specifies how to behave when text scrolls beyond the screen of the text widget that it is interacting with. The init procedure loads the mode variable into its local namespace using the global command and uses a switch statement to set the value of the global variable, action. In this example, the switch command tests whether "$mode" matches the first word on each line in the list, and performs the action specified on the second word of each line. The default value is specified at the bottom of the list and defines the action performed if no match is found. Switch accepts 4 options: -exact, which requires a case-sensitive match, -glob, which uses a glob-style pattern match, -regexp, which uses regular-expression style matching, and --, which indicates the end of options, and is typically used if the pattern being matched has a "-" as a prefix. Note: We could have used an if-elseif-else conditional chain rather than the switch statement:
The final thing that the init procedure does is call the put_text procedure. The put_text procedure reads in the value of action that was set in the init procedure, executes apachectl with the appropriate argument as specified by action, and prints apache's output to the .bottom.main text widget.
The put_text procedure introduces 3 new commands: First, it sets the value of a variable, f, to the output of the open command. Open can be used to open a file, pipe stream or serial port and returns an identifier which can be used for reading, writing, or closing a stream. Since the first character following the open is a pipe "|", $apachectl $action is treated as a command, and is executed as though the exec had been given. The r specifies that the stream is read-only. Other parameters are as follows:
The second new command is while. While is a typical while loop which executes a body of arguments so long as the specified condition is met. In this case, while will read a line of input and save it to the variable x until there is nothing left to read. The insert command inserts each line of input to the zero'th character of line 1 (1.0) of the .bottom.main text widget. 5. ConclusionsThis was a fairly quick and dirty introduction by example to creating graphical user interfaces for command line applications with Tcl and Tk. In addition to the language's basic syntax, we learned about procedures, several of the basic widget sets, progam control flow, some of Tcl's logical operators, and the basics of how to insert text into a text widget. The simplicty of Tcl's syntax makes it a very easy language to learn and to build in. Our final example was built with less than 40 lines of code. If you want to go beyond the basics described here, the gridded window provides a more advanced mechanism for building window geometries than the packer that we used for this tutorial. Also, Tcl can be used for more than simply scripting graphical interfaces. For example, Don Libes' expect programming language, which provides facilities for constructing a programmed dialogue with interactive programs, was written in Tcl and C. The next section discusses where to go next for more resources on Tcl and Tk. 6. Further ReadingReaders interested in learning more about Tcl and Tk are encouraged to followup this article with some of the resources listed below:
7. About the AuthorSalvador Peralta is the Systems Administrator for the Mark O. Hatfield Library at Willamette University. He is a regular contributor to the Linux Documentation project and lives in Oregon with his wife and two dogs. Salvador welcomes comments and questions via email to speralta [at] willamette [dot] edu. |