Example pure Tcl/Tk program

To show some of what can be done with Tcl/Tk, we will show an example of part of a GUI for an 8-queens program. Most people will be familiar with the 8-queens problem: how to place 8 queens on a chess board such that they do not attack each other according to the normal rules of chess.

Our example will not be a program to solve the 8-queens problem (that will come later in the tutorial) but just the Tcl/Tk part for displaying a solution. The code can be found in library('tcltk/examples/ex18.tcl').

The way an 8-queens solution is normally presented is as a list of numbers. The position of a number in the list indicates the column the queens is placed at and the number itself indicates the row. For example, the Prolog list [8, 7, 6, 5, 4, 3, 2, 1] would indicate 8 queens along the diagonal starting a column 1, row 8 and finishing at column 8 row 1.

The problem then becomes, given this list of numbers as a solution, how to display the solution using Tcl/Tk. This can be divided into two parts: how to display the initial empty chess board, and how to display a queen in one of the squares.

Here is our code for setting up the chess board:

% ex18.pl
#! /usr/bin/wish proc setup_board { } { # create container for the board frame .queens # loop of rows and columns for {set row 1} {$row <= 8} {incr row} { for {set column 1} {$column <= 8} {incr column} { # create label with a queen displayed in it label .queens.$column-$row -bitmap @bitmaps/q64s.bm -relief flat # choose a background color depending on the position of the # square; make the queen invisible by setting the foreground # to the same color as the background if { [expr ($column + $row) % 2] } { .queens.$column-$row config -background #ffff99 .queens.$column-$row config -foreground #ffff99 } else { .queens.$column-$row config -background #66ff99 .queens.$column-$row config -foreground #66ff99 } # place the square in a chess board grid grid .queens.$column-$row -row $row -column $column -padx 1 -pady 1 } } pack .queens } setup_board

The first thing that happens is that a frame widget is created to contain the board. Then there are two nested loops that loop over the rows and columns of the chess board. Inside the loop, the first thing that happens is that a label widget is created. It is named using the row and column variables so that it can be easily referenced later. The label will not be used to display text but to display an image, a bitmap of a queen. The label creation command therefore has the special argument -bitmap @q64s.bm, which says that the label will display the bitmap loaded from the file q64s.bm.

The label with the queen displayed in it has now been created. The next thing that happens is that the background color of the label (square) is chosen. Depending on the position of the square it becomes either a “black” or a “white” square. At the same time, the foreground color is set to the background color. This is so that the queen (displayed in the foreground color) will be invisible, at least when the board is first displayed.

The final action in the loop is to place the label (square) in relation to all the other squares for display. A chess board is a simple grid of squares, and so this is most easily done through the grid geometry manager.

After the board has been set up square-by-square it still needs to be displayed, which is done by pack-ing the outermost frame widget.

To create and display a chess board widget, all that is needed is to call the procedure


which creates the chess board widget.

Once the chess board has been displayed, we need to be able to take a solution, a list of rows ordered by column, and place queens in the positions indicated.

Taking a topdown approach, our procedure for taking a solution and displaying is as follows:

     proc show_solution { solution } {
         set column 1
         foreach row $solution {
             place_queen $column $row
             incr column

This takes a solution in solution, clears the board of all queens, and then places each queen from the solution on the board.

Next we will handle clearing the board:

     proc clear_board { } {
         for { set column 1 } {$column <= 8} {incr column} {
             reset_column $column
     proc reset_column { column } {
         for {set row 1 } { $row <= 8 } {incr row} {
             set_queens $column $row off
     proc set_queens { column row state } {
         if { $state == "on" } {
             .queens.$column-$row config -foreground black
         } else {
             .queens.$column-$row config
             -foreground [.queens.$column-$row cget -background]

The procedure clear_board clears the board of queens by calling the procedure reset_column for each of the 8 columns on a board. reset_column goes through each square of a column and sets the square to off through set_queens. In turn, set_queens sets the foreground color of a square to black if the square is turned on, thus revealing the queen bitmap, or sets the foreground color of a square to its background color, thus making the queens invisible, if it is called with something other than on.

That handles clearing the board, clearing a column or turning a queen on or off on a particular square.

The final part is place_queen:

     proc place_queen { column row } {
         reset_column $column
         set_queens $column $row on

This resets a column so that all queens on it are invisible and then sets the square with coordinates given in row and column to on.

A typical call would be:

     show_solution "1 2 3 4 5 6 7 6 8"

8-Queens Display In Tcl/Tk

which would display queens along a diagonal. (This is of course not a solution to the 8-queens problem. This Tcl/Tk code only displays possible queens solutions; it doesn't check if the solution is valid. Later we will combine this Tcl/Tk display code with Prolog code for generating solutions to the 8-queens problem.)

Send feedback on this subject.