An introduction to Tk

ArticleCategory:

Software Development

AuthorImage:

[Photo of the author]

TranslationInfo:

original in fr Charles Vidal
fr to enCharles Vidal
en to es

AboutTheAuthor:

President of a gastronomic lug in Paris. He likes the GNU and Open Source philosophy, because they both allow people to share knowledge. He would like to find time to play Saxophone.

Abstract:

This article introduces you to the features of the Tcl graphical toolkit: Tk. We shall see how easy it is to make a GUI (Graphical User Interface) with a few lines of code.

ArticleIllustration:

Tcl Tk logo

ArticleBody:

Tk the Graphical Toolkit for Tcl :WISH

Tk has been created to give the Tcl language a graphical toolkit. We usually call it Tcl/Tk; it is pronounced Tikel/Tikey.

It is a mutli-platform graphical toolkit with the look of the native os. It combines perfectly with the Tcl language which is also mutli-platform. The great advantage of Tcl/Tk is its simplicity. The couple allows you to rapidly create portable applications. Just as tclsh exists for tcl, there is wish for Tk.

Hello world !

As an introduction, I'll show you how to program a classical example. It shows the possibilities and the simplicity of the Tcl/Tk implementation.
pack [ label .l -text "Bonjour monde" ]

Just compare this to a gtk program written in C:
#include <gtk/gtk.h>
Int main( int   argc,
          char *argv[] )
{
   /* GtkWidget is the storage type for widgets */
    GtkWidget *window;
    GtkWidget *button;

    gtk_init(&&argc, &&argv);
   /* create a new window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    button = gtk_button_new_with_label ("Bonjour Monde");
    gtk_container_add (GTK_CONTAINER (window), button);
    gtk_widget_show (button);

    /* and the window */
    gtk_widget_show (window);
    gtk_main ();

    return(0);
}
... or compare it to Motif
/* COMPILE  cc xmhello.c -L/usr/X11R6/lib  -lXm -lXt -lX11 -lXmu -o xmhello */

#include <Xm/PushB.h>
#include <Xm/Form.h>

/* Widget */

	Widget      main_widget, pushb;

main(int argc, char **argv)
{
	Widget      form;
	Arg         args[1];
	int         ac = 0;
	XmString    label_str;

	main_widget = XtInitialize(argv[0], "test", NULL, 0, &&argc, argv);
	label_str = XmStringCreate("Bonjour Monde", XmSTRING_DEFAULT_CHARSET);
	XtSetArg(args[ac], XmNlabelString, label_str);
	ac++;
	pushb = XmCreatePushButton(main_widget, "hello", args, ac);
	XtManageChild(pushb);
	XtRealizeWidget(main_widget);
	XtMainLoop();
}
But it is hard to compare the source of a scriptic language and a compiled one. We must as well consider other aspects like the size of the application in memory. So, all we can say is that making the classical "Hello World" program with Tk is fairly easy. More than the appearance of the toolkit, we should notice the concept and the ideas.
There are some extensions to modify the look of the Tk Widget, for example Qtk, and TkStep.
It is interesting to note that the Tk Toolkit can be used with a lot of languages. And like a lot of scriptics languages Tcl-Tk is multi-platform.

Tk graphical object creation

This toolkit provides you with a set of basic graphical objects (widgets) in order to build a GUI.

To watch a demo of all the available widgets, go into the directory /usr/local/lib/tk8.0/demos/ and launch widget.

The set of these graphical objects is limited (there are no tree widget, combobox ...). However people or companies made some valuable extensions, the most famous is : Tix the widgets Incr Tcl and recently the great BWidget, all of the extensions are available from the scriptics web site.

The tree structure of the graphical object in the gui

GUIs are build as a tree of windows (rectangle) with their own properties.
Nodes from the tree structure are containers for graphical objects like a button for instance. A tree structure is described in the Tcl/Tk language as follows: When the wish program is launched, a window appears. This is the root window named . (dot).

Creation of graphical objects: "hello world"

To create any graphical object, the tcl command looks like:
name_object .(name_container.)*name_graphical_object [property value]
Example: label .mylabel -text "hello world"

As you can see, the action is carried out by giving the name of the graphical object to create, here a .label, next the name of the container, where . is the root window, and at the end you specify the properties (-text " hello Word "). We notice here that these arguments can be accessed or modified constantly after the creation of the graphical object.


And now the label has the text "Bonjour Monde in french :) "
You can display it with the command: puts [label .mylabel -text "hello world"] The label can display the result of a command:
.mylabel configure -text " This is the date [exec date ]"
To list the options you can pass to configure for a given widget, type: ".mylabel configure" using Wish in interactive mode.
This gives an error and shows the set of options you can use.

Layouts: Manager of the widgets positions.

You might say Nothing happens, nothing shows on the display

Indeed, you just have created an object of the label type, but you did not ask to display it. This is because the display of this object requires information you did not yet specify; that is in which layout (a layout is a manager which helps positioning windows) you wish to put this object.

Here are the available layouts:
  1. The packer
  2. The placer
  3. The grid
Let us choose the simplest: the packer. It adds the objects inside himself according to the specifications of the user. More about that, later.

Fully featured Hello Word
label .mylabel -text "hello world"
pack .mylabel

Or in one line
pack [label .mylabel -text "hello world"]


The button Let us study the case of a button:

button .myboutton -text "hello world" -command "exit"
pack .myboutton

We notice that the button has a "command" property as an argument (only one). It is the tcl command to execute when the user clicks the button. In this example, the command to execute is exit , this causes the program to quit.

Associating graphical objects and variables.

The power of Tcl/tk is its great ease of associating graphical objects to a variable corresponding to their change of state. We do not need to call a function to know the state of a graphical object. In the same way, if one changes the variable, it will directly influence the graphical object. As you can see, this makes programming very straightforward, and makes it possible to write much less code.

Example

The checkbuttons and radiobuttons.

  1. A checkbutton is a button reflecting a state.
    This button shows a boolean state (set or not). We can associate a variable to it which takes the value 0 (defaut) or 1 according to the state of the checkbutton. But we can also define the value the variable takes according to the state of the checkbutton.
    Example:

    checkbutton $w.b1 -text "Wipers OK" -variable wipers -relief flat -onvalue "Ok" -offvalue "not Ok"

  2. A radiobutton is a button reflecting the state of a set of buttons.
    The radiobuttons are often grouped, this allows to choose one among others. In order to group them together, you have to assign them the same variable name and give it the value this variable takes when the radiobutton is selected.
    Exemple:



    radiobutton .b1 -text "Premier " -variable size -value 1
    radiobutton .b2 -text "Second " -variable size -value 2

Entry
This widget is a line of text that the user can fill. You can assign a variable to an entry with -textvariable.
Example:


entry .e -textvariable toto -width 40

Layout manager

Usually graphical objects are put in a frame. Be careful not to use more than one layout manager in the same frame. There are three layout managers:
  1. The packer: It positions the graphical objet according to an orientation:
    1. top
    2. bottom
    3. right
    4. left

    Example:
    pack [ button .b1 -text top ] -side top
    pack [ button .b2 -text bottom ] -side bottom
    pack [ button .b3 -text right ] -side right
    pack [ button .b4 -text right ] -side left
    

    We can also configure the expansion of the widget inside: option -expand (yes|no) -fill ( x|y| both)

  2. The placer: We can place a graphical objet at the x y position.

    Example: place [ label .l -text "With Place"] -x 100 -y 100 .l configure -bg red

  3. The grid places widgets on a virtual grid made of rows and columns. It is the ideal layout for listboxes or multiple line editboxes. The syntax is: First create widgets, for instance .e_name then display them using:

    label .mainlbl2 -text "Label 2" -bd 2 -relief sunken
    grid .mainlbl2 -row 0 -column 1 -sticky news
    label .mainlbl1 -text "Label 1" -bd 2 -relief raised
    grid .mainlbl1 -row 0 -column 0 -sticky news
    label .mainlbl3 -text "Label 3" -bd 2 -relief solid
    grid .mainlbl3 -row 1 -column 0
    label .mainlbl4 -text "Label 4" -bd 2 -relief groove
    grid .mainlbl4 -row 1 -column 1

The list of the created widgets

We can get the list the graphical objects created with the command winfo.

 winfo exists name_object
But we can get the list of all widget created with the command:
winfo children .
This functionality of tcl/tk does not exist in the other compiled graphical toolkits ( gtk, awt, motif ...).

Events

Tk treats the event and executes a command when -command was given to it. But there are cases where one wishes to manage events more precisely or manage several events for one widget. The canvas is such a widget.



bind name_of_widget name_of_event tcl_code.

A little example

We are going to make a GUI to see the content of a tar (gz) file .
The interface is made of a list and two scrollbars.
Here is a description of the structure of the tcl script:
  1. We build the gui (proc makegui).
  2. We insert all the paths of the widget in a hashtable tabgui.
  3. We scan the number of arguments; if it's equal to zero the application exits.
  4. We open the handle from a pipe with the program tar -tzvf name_file_tar.
  5. We insert each line read from the pipe into the list.
We capture the keyboard event "Control C" to quit the application:
bind all <Control-c> {destroy .}

This is only a small example, we can improve it. We can test if the file is compressed or not with the tcl command string first. If the user does not give any argument, then a file chooser appears in order to select the tar file. We could make it more sophisticated by creating a popmenu allowing to act on files within the archive. Finally, we could have a menu bar at the top allowing the user to open a new file, create an archive ...
#!/bin/sh
# the next line restarts using wish \
exec wish8.0 "$0" "$@"

global tabgui

proc makegui { } {
global tabgui
#
# Creation of the  scrollbars for the list
# then the horizontal scrollbar is placed right, filling up horizontally the window
# and the vertical scrollbaris placed below, filling up vertically the window
#
set tabgui(scrollv) [scrollbar .scrollv -command ".list yview"]
pack $tabgui(scrollv) -side right -fill y
set tabgui(scrollh) [scrollbar .scrollh -command ".list xview" -orient horizontal ]
pack $tabgui(scrollh) -side bottom -fill x
#
# Creation of the list associating it to the scrollbar
#
#
set tabgui(list) [listbox .list  \
                -yscroll "$tabgui(scrollv) set" \
                -xscroll "$tabgui(scrollh) set" \
                -relief sunken -width 20 -height 20 \
        -setgrid yes ]
pack $tabgui(list) -side left -fill both -expand yes
wm minsize . 1 1
}

#
# Creation of the GUI
#
makegui

if $argc>0 {set tarfile [lindex $argv 0]} else {puts stderr "tar file missing" ; exit} 
set command "tar -tzvf $tarfile"
set tube [ open |$command r] 
 while {![eof $tube]} {
    set tarresult  [ gets $tube ] 
    $tabgui(list)  insert end $tarresult
}

bind all <Control-c> {destroy .}

Ressources