Experiments in making embellished GUI's

uniquename - 2012aug13

Before retiring in 2005, I had the good fortune to work for about 8 years on SGI/IRIX (Unix) workstations. On those workstation desktops, there was the concept of 'toolchests' by which one could startup applications.

The toolchests served the same function as the 'Start' menus on PC's. The main difference in implementation on the SGI desktop is that the main toolchest started at the top-left of the screen instead of the bottom left --- and they had a different look --- an X-Motif look.

After I retired, I thought I would like to make toolchests of apps and utilities on my Linux PC's. On the SGI's, to add toolchests, one had to make text files of a certain format --- to define the 'drawers' of the toolchest, as well as define separator lines and label (group heading) lines, where needed.

There was an underlying, compiled toolchest-making program on the SGI's that would make the added toolchests from those text files.

Unfortunately, there was no such toolchest making program on my PC's --- at least not a program that I could get into and make work how I wanted it to work.

In fact, I had a bad experience with application menus, back around 2005, when using Mandriva Linux with a KDE desktop. I used the KDE menu editor to make some additions to the app menus, but there was a bug in the KDE menu system. Some of my additions would not show. And when I tried to add them again, I got a message saying they were already there. I was caught between a rock and a hard place.

I decided then and there that I was going to make my own toolchest making system --- using Tcl-Tk to make the toolchests. I devised a simple format for the text files that would define the toolchests. I just had to make a Tcl-Tk script that would read the lines of the 'chest definition' files and make toolchests.

I wasn't sure that it was possible to make a Tk script that would read such a text file and generate a 'toolchest' GUI within a fraction of a second. Let me just say that I was pleasantly surprised.

I decided to make the toolchest 'drawers' with 'button' widgets. I managed to crank out a 'make_chest.tk' script that had several sections in a read-loop in the script --- for making

  - 'active' drawers
  - inactive (grayed out) drawers
  - label (group-heading) drawers
  - separator lines (made with thin 'frame' widgets).

My Tk-button toolchests ended up looking something like this.

make_toolchest_prototype_buttonsWithBullets_screenshot_273x197.png

You can see screenshots (and descriptions) of the more feature-rich toolchests of my 'Freedom Environment' (FE) software in the 'feAppMenus' and 'feHandyTools' sections of a screenshots-gallery page [1 ].

With the FE toolchests,the user has the ability to change the color scheme in the toolchests (via a Tk color selector GUI like the one described at A non-obfuscated color selector GUI). The color scheme of the toolchests is changed via the 'tk_setPalette' command.

In the FE toolchests, the user can also change (or eliminate) the 'bullet' images used on the left of each drawer.

This is all nice --- but not nice enough. I am hoping to make more attractive toolchests --- ones that even Steve Jobs would have envied --- by making variations on my Tk-button 'make_chest.tk' script.

I was hoping to use the '-compound' parameter of the 'button' command to provide images BEHIND the text on the drawers --- not just the 'bullet' images to the LEFT of the text. But I hit a problem with the '-compound' parameter that is documented in a 'text on image' wish that I posted on the Tk 9.0 Wishlist page of this wiki.

Since that wish may not be implemented in my lifetime (and perhaps never), I have been experimenting with using the 'canvas' widget to make 'embellished' toolchests.

Here are three images corresponding to three basically different techniques that I have tried so far. A description of the basics of each toolchest implementation is under each image.

make_toolchest_prototype_gradientLinesOnCanvases_screenshot_411x233.png

TOOLCHEST DRAWERS ARE MADE WITH: 'canvas' widgets

DRAWER ACTIONS ARE PROVIDED BY: button1-release bindings on the canvas widgets

TOOLCHEST/DRAWER DECORATION IS PROVIDED BY: 'create line' commands on the canvas widgets --- to provide COLOR GRADIENT BACKGROUNDS for the 'drawers' ; uses a 'DrawGradient' proc based on Tcl-Tk scripts by B. Oakley and Damon Courtney and published at Drawing Gradients on a Canvas on this wiki.

__________________________________________________________________________

make_toolchest_prototype_imagesOnCanvases_screenshot_371x251.png

TOOLCHEST DRAWERS ARE MADE WITH: 'canvas' widgets

DRAWER ACTIONS ARE PROVIDED BY: button1-release bindings on the canvas widgets

TOOLCHEST/DRAWER DECORATION IS PROVIDED BY: 'image create' for a background image on the canvas widgets. A rectangular GIF image file is used to make an image variable with 'image create photo'. That image variable is used in 'image create' statements to put the background on the drawers.

At first, I used an 'eval' technique to load the text strings (for the drawers) into variables such as TXTstring1, TXTstring2, ... --- and to make canvas IDs such as .can1, .can2, ...

To avoid a lot of 'escaping' of special characters (double-quotes, braces, brackets), I ended up loading the text strings for the drawers into an array variable.

In addition to putting the text strings into an array variable, I ended up defining the canvas widgets as .can(1), .can(2), ... rather than .can1, .can2, ...

In a loop over the array argument (drawer count), the canvases are defined-and-packed (with image and text put on each canvas). Also in the loop, the bindings to button1-release are defined.

_______________________________________________________________________________

make_toolchest_proto_imgOnOneCanvas_textlineTagBindings_screenshot_338x193.png

TOOLCHEST DRAWERS ARE MADE WITH: text lines, each with a tag for a binding (on ONE big canvas)

DRAWER ACTIONS ARE PROVIDED BY: button1-release bindings on the text tags

TOOLCHEST/DRAWER DECORATION IS PROVIDED BY: 'image create' for a background image on the one big canvas widget. A rectangular GIF image file is used to make an image variable with 'image create photo'. That image variable is used in the 'image create' statement to put the background on the ONE canvas widget of this GUI.

(This background image is rather grainy. We will all be happier when the PNG image import capability is built-in to Tcl-Tk --- and the PNG-capable version of the wish interpreter is wide-spread, so that it will be easy for any 'vanilla-Tk' user to avoid the appearance problems of GIF images limited to a 256 color palette.)

_____________________________________________________________________________

And here is a description of the basics of the Tk-button toolchest --- the first image above.

TOOLCHEST DRAWERS ARE MADE WITH: 'button' widgets

DRAWER ACTIONS ARE PROVIDED BY: the '-command' parm of 'button'

TOOLCHEST/DRAWER DECORATION IS PROVIDED BY: the 'tk_setPalette' command, with an optional 'bullet' image on the left of each button/drawer, applied with the '-compound left' parameter of the button widget.

__________________________________________________________________________

CODE to create the 4 IMAGES ABOVE:

Since I want to 'give back' to the Tcl-Tk community --- and help out any 'newbies' out there trying to get up to speed in Tcl-Tk programming, here is the code corresponding to the 4 images above.


 CODE FOR THE BUTTONS/COLOR-PALETTE/BULLETS toolchest :
#!/usr/bin/wish -f
##
## SCRIPT: make_chest_proto_buttonsInFrames.tk
##
##+############################################################################
## DESCRIPTION: This Tk script
##
##    MAKES TOOLCHEST DRAWERS WITH: 'button' widgets
##    PROVIDES DRAWER ACTIONS WITH: the '-command' parm of 'button'
##    PROVIDES TOOLCHEST/DRAWER DECORATION WITH: 'tk_setPalette' command, with
##              an optional 'bullet' image on the left of each button/drawer.
##+###########################################################################
## REFERENCES:
##         This script is based on (is simplified from)
##         the original 'make_chest.tk' script of the feAppMenus subsystem
##         of the Freedom Environment software.  See www.freedomenv.com.
##
##+###################################################################
## Created: 2012jul26 
##+###################################################################


##+#################################################
## Set window titles, location, and resize behavior.
##+#################################################

wm title    . "Apps Toolchest"
wm iconname . "Apps"
wm geometry . +150+30
wm resizable . 0 0


##+######################################
## Set a color scheme for the toolchest
## --- esp. its buttons, frames, borders.
##+######################################

tk_setPalette "#cc9933"


##+######################################
## Set font to use in the button widgets.
##+######################################

set feFONT_button "-family {comic sans ms} -size -14 -weight bold -slant roman"

eval font create fontTEMP_button  $feFONT_button


##+####################################################################
## Set button (drawer) geometry parameters --- xy padding, borderwidth.
##+####################################################################

set fePADX_button 0
set fePADY_button 0
set feBDwidth_button 0


##+#############################################################
## Set a bullet image to use on the left of each button (drawer)
## --- for a little decoration.
##+#############################################################

# set imgID4bullet [image create photo -file "./bullet_grayish_20x15_transp.gif"]
  set imgID4bullet [image create photo -file "./bullet_orangish_20x15_transp.gif"]

##+########################################################
## Define and pack frame(s) to contain the drawers.
##
## We do not define the bottom-config-buttons subframe of
## the left frame.  And we do not define the right frame.
## Those are frames used in the full implementation of
## the feAppMenus and feHandyTools FE subsystems. 
##+########################################################

## FOR TESTING:
# set feRELIEF_frame raised
# set feBDwidth_frame 2

set feRELIEF_frame flat
set feBDwidth_frame 0


frame .fRleft             -relief $feRELIEF_frame   -bd $feBDwidth_frame

frame .fRleft.fRdrawers   -relief $feRELIEF_frame   -bd $feBDwidth_frame


pack  .fRleft \
            -side left \
            -anchor nw \
            -fill none \
            -expand 0

pack  .fRleft.fRdrawers \
            -side top \
            -anchor nw \
            -fill both \
            -expand 1

## Other frames in the feAppMenus and feHandyTools toolchest-making scripts:
##
## .fRleft.fRbottom
## .fRright.fRdrawers
## .fRright.fRbottom


##+#######################################################
## Set the text strings to be put on the buttons/drawers
## --- in variables. 
##
## We  changed
##   textSTRING1, textSTRING2, ...
## to array  variables ...
##   textSTRING(1), textSTRING(2), ...
##
## In a 'production' script, these vars could be set by reading
## lines of a 'chest definition' file, for each toolchest.
##+#######################################################

set textSTRING(1) "Seamonkey - web browser"
set textSTRING(2) "Thunderbird - email client"
set textSTRING(3) "Filezilla - FTP client"
set textSTRING(4) "mtpaint - image editor"

set Ndrawers 4


##+#######################################################
## In a loop over the number of drawers,
## define and pack the button widgets (toolchest drawers).
##
## This includes putting the text string on each button,
## putting the 'bullet' image on the left of each drawer,
## and setting a command to use for each drawer.
##+#######################################################

set drawerCNT 1

while { $drawerCNT <= $Ndrawers } {

   set drawerMSG "Toolchest drawer $drawerCNT was clicked"

   ## FOR TESTING:
   #  puts "drawerMSG: $drawerMSG"

   button .fRleft.fRdrawers.butt($drawerCNT) \
      -text "$textSTRING($drawerCNT)" \
      -anchor w \
      -font fontTEMP_button \
      -relief flat \
      -bd $feBDwidth_button \
      -padx $fePADX_button \
      -pady $fePADY_button \
      -image $imgID4bullet \
      -compound left \
      -command " tk_dialog .dialog1 \
        \"Dear user:\" \"$drawerMSG\" info 0 OK "

   pack  .fRleft.fRdrawers.butt($drawerCNT) \
      -side top \
      -anchor nw \
      -fill both \
      -expand 1

   set drawerCNT [expr $drawerCNT + 1]

}
## END OF 'while' LOOP


 CODE FOR THE COLOR-GRADIENT-DRAWERS toolchest :

(Each drawer is drawn by a DrawGradient proc --- hence this is not the most efficient method. Better to capture a color gradient in an image file and use the image file with the following two techniques. And hence my A color-gradient-button-maker GUI page on this wiki.)

#!/usr/bin/wish -f
##
## SCRIPT: make_chest_proto_gradientLinesInCanvases.tk
##
##+###########################################################################
## DESCRIPTION: This Tk script
##
##    MAKES TOOLCHEST DRAWERS WITH: 'canvas' widgets
##    PROVIDES DRAWER ACTIONS WITH: button1-release bindings on the canvas widgets
##    PROVIDES TOOLCHEST/DRAWER DECORATION WITH: 'create line' commands on
##              the canvas widgets --- to provide COLOR GRADIENT BACKGROUNDS for
##              the 'drawers' ; uses a 'DrawGradient' proc based on
##              a Tcl-Tk script by B. Oakley and Damon Courtney and published at
##              https://wiki.tcl-lang.org/6100 - Drawing color gradients.
##+###########################################################################
## Created: 2012jul25
##+###################################################################


##+#################################################
## Set window titles, location, and resize behavior.
##+#################################################

wm title    . "Apps Toolchest"
wm iconname . "Apps"
wm geometry . +150+30
wm resizable . 0 0


##+########################################################
## Set font to use for the text in the 'toolchest drawers'.
##    (A variable width font, like that normally used 
##     on button widgets, would be appropriate/nice.)
##+########################################################

set feFONT_button "-family {comic sans ms} -size -14 -weight bold -slant roman"

eval font create fontTEMP_drawer  $feFONT_button


##+########################################################
## Set the color-gradient parameters for the 'drawers',
## which are made with 'canvas' widgets in this script.
##
##   The color gradients are drawn in the canvas widgets
##   using  'create line' commands.
##+########################################################
 
set leftCOLOR yellow
set rightCOLOR red
set colorDIRECTION x


##+############################################################
## Set a border width for the drawers --- which are like
## 'buttons', made with 'canvas' widgets in this script.
##+############################################################

# set BDwidth_canvas 4
  set BDwidth_canvas 0


##+########################################################################
## Put the text strings --- that are to be put on the drawers
## (canvas widgets) --- in variables.
##
## In a 'production' script, these vars could be set by reading lines
## of a 'chest definition' file, for each toolchest.
##
## We changed vars
##   textSTRING1, textSTRING2, ...
## to array  variables ...
##   textSTRING(1), textSTRING(2), ...
## elimnating the need to use 'eval' which, in turn, required escaping
## special characters like double-quotes, braces, and brackets.
##+########################################################################

set textSTRING(1) "Seamonkey - web browser"
set textSTRING(2) "Thunderbird - email client"
set textSTRING(3) "Filezilla - FTP client"
set textSTRING(4) "mtpaint - image editor"
set textSTRING(5) "SciTE - text editor"
set textSTRING(6) "gnome-screenshot - image capture"
set textSTRING(7) "gnome-terminal - command-interpreter window"
set textSTRING(8) "gcalctool - calculator"

set Ndrawers 8


##+########################################################################
## Set the width (in pixels) of the drawers --- according to the
## longest text string to be placed on the drawers (canvases). 
##
## We use 'font measure' to get the string widths.
## Note that the font used for the text is a factor in determining the widths.
##
## We use the max-string-width and add about 10 pixels to get drawers (canvases)
## a little wider than the longest text string on the drawers.
##+########################################################################

set drawerCNT 1
set strMaxWidthPx 15

while { $drawerCNT <= $Ndrawers } {

   set strWidthPx [font measure fontTEMP_drawer "$textSTRING($drawerCNT)"]

   ## FOR TESTING:
   #  puts "strWidthPx = $strWidthPx  drawerCNT = $drawerCNT"
   #  puts "$textSTRING($drawerCNT)"
   #  puts ""

   if { $strWidthPx > $strMaxWidthPx } {
      set strMaxWidthPx $strWidthPx
   }

   set drawerCNT [expr $drawerCNT + 1]
}

set drawerWidthPx [expr $strMaxWidthPx + 10]


##+########################################################################
## Set the drawer height (in pixels) --- according to the height of
## the text strings.
##     (Depends on the font used for the strings. We use 'font metrics'
##      to get the 'linespace' height.)
##+########################################################################

set drawerHeightPx [font metrics fontTEMP_drawer -linespace] 

## FOR TESTING:
#  puts "drawerHeightPx = $drawerHeightPx"


##+########################################################################
## Set the position in each canvas widget (relative to the top left of each
## canvas widget) at which the left side of each text string will be located.
##
## We add the borderwidth of the canvas onto the x and y text offsets.
##
## We use about 6 pixels of the x offset. 6 might be a bit much.
##+########################################################################

set yLocTextPx [expr $BDwidth_canvas + ($drawerHeightPx / 2)]

set xLocTextPx [expr $BDwidth_canvas + 6]


##+############################################################
## In a loop over the number of drawers,
## define and pack the canvas widgets (toolchest drawers).
##
## We put the canvas IDs into array vars .can(1), .can(2), ...
##+############################################################

set drawerCNT 1

while { $drawerCNT <= $Ndrawers } {

   canvas .can($drawerCNT) \
      -relief raised \
      -borderwidth $BDwidth_canvas \
      -height $drawerHeightPx \
      -width $drawerWidthPx
 
   pack .can($drawerCNT) \
         -side top \
        -anchor nw \
        -fill none \
        -expand 0

   set drawerCNT [expr $drawerCNT + 1]

}
## END OF 'while' LOOP to define-pack canvas 'drawers'


##+#######################################################
## Define BINDINGS:
##  - one button1-release binding for each canvas drawer
##+#######################################################

set drawerCNT 1

while { $drawerCNT <= $Ndrawers } {

   set drawerCMD "tk_dialog .dialog1 \
          {Dear user:} {Toolchest drawer $drawerCNT was clicked} info 0 OK"

   ## FOR TESTING:
   #  puts "drawerCMD: $drawerCMD"

   bind .can($drawerCNT) <ButtonRelease-1> "$drawerCMD"

   set drawerCNT [expr $drawerCNT + 1]
}
## END OF 'while' LOOP for bindings


##+################################################################
## Define PROCS:
##  - 'DrawGradient' to draw color gradient on each canvas drawer
##+################################################################

proc DrawGradient {win axis col1Str col2Str} {

   # if {[winfo class $win] != "Canvas"} {
   #   return -code error "$win must be a canvas widget"
   # }

   # $win delete gradient

   set width  [winfo width $win]
   set height [winfo height $win]
   switch -- $axis {
      "x" { set max $width; set x 1 }
      "y" { set max $height; set x 0 }
      default {
         return -code error "Invalid axis $axis: must be x or y"
      }
   }

   if {[catch {winfo rgb $win $col1Str} color1]} {
      return -code error "Invalid color $col1Str"
   }

   if {[catch {winfo rgb $win $col2Str} color2]} {
      return -code error "Invalid color $col2Str"
   }

   ## FOR TESTING:
   #  puts "color1 = $color1"
   #  puts "color2 = $color2"

   foreach {r1 g1 b1} $color1 break
   foreach {r2 g2 b2} $color2 break

   ## FOR TESTING:
   #  puts "r1 = $r1"
   #  puts "g1 = $g1"
   #  puts "b1 = $b1"
   #  puts "r2 = $r2"
   #  puts "g2 = $g2"
   #  puts "b2 = $b2"

   set rRange [expr $r2.0 - $r1]
   set gRange [expr $g2.0 - $g1]
   set bRange [expr $b2.0 - $b1]

   set rRatio [expr $rRange / $max]
   set gRatio [expr $gRange / $max]
   set bRatio [expr $bRange / $max]

   for {set i 0} {$i < $max} {incr i} {
      set nR [expr int( $r1 + ($rRatio * $i) )]
      set nG [expr int( $g1 + ($gRatio * $i) )]
      set nB [expr int( $b1 + ($bRatio * $i) )]

      set col [format {%4.4x} $nR]
      append col [format {%4.4x} $nG]
      append col [format {%4.4x} $nB]

      ## FOR TESTING:
      #  puts "col = $col"

      if {$x} {
         $win create line $i 0 $i $height -tags gradient -fill #${col}
      } else {
         $win create line 0 $i $width $i -tags gradient -fill #${col}
      }
   }
   # bind $win <Configure> [list DrawGradient $win $axis $col1Str $col2Str]
   # return $win
}
## END OF proc DrawGradient


##+#######################################################
## Additional GUI INITIALIZATION:
##  - draw color-gradients onto (and put text strings on)
##    the canvas 'drawers'
##+#######################################################

## Before using the 'DrawGradient' proc to put the color
## gradients in the canvas 'drawers', we need to call 
## 'update' to establish the width and height of
## the canvas widgets, because DrawGradient uses
## 'winfo' to get the width and height of each canvas.

update

## Now we draw the color gradients and put on the text strings.

set drawerCNT 1

while { $drawerCNT <= $Ndrawers } {

   DrawGradient .can($drawerCNT) $colorDIRECTION $leftCOLOR $rightCOLOR

   .can($drawerCNT) create text \
      $xLocTextPx $yLocTextPx \
      -anchor w \
      -font fontTEMP_drawer \
      -fill black \
      -justify left \
      -text "$textSTRING($drawerCNT)"

   set drawerCNT [expr $drawerCNT + 1]
}
## END OF 'while' LOOP for drawing the color-gradient and putting text on drawers


 CODE FOR THE ONE-BACKGROUND-IMAGE-PER-DRAWER toolchest :
#!/usr/bin/wish -f
##
## SCRIPT: make_chest_proto_imageCreateInCanvases_txt-can-arrays.tk
##
##+############################################################################
## DESCRIPTION: This Tk script
##
##    MAKES TOOLCHEST DRAWERS WITH: 'canvas' widgets
##    PROVIDES DRAWER ACTIONS WITH: button1-release bindings on the canvas widgets
##    PROVIDES TOOLCHEST/DRAWER DECORATION WITH: 'image create' for a background
##                                                image on the canvas widgets.
##                A rectangular GIF image file is used to make an image var
##                with 'image create photo'. That image var is used in
##                'image create' statements to put the background on the drawers.
##
##    This script puts the text strings for the buttons in an array variable.
##    Then, in a loop over the array argument (drawer count), the canvases are
##    defined-and-packed (with image and text put on each canvas).
##    In a loop over the array argument, the bindings are also defined.
##
##    In addition to putting the text strings in an array variable,
##    this script defines the canvas widgets as .can(1), .can(2), ...
##    rather than .can1, .can2, ...
##+############################################################################
## Created: 2012aug08 
##+#########################################################################


##+########################################################################
## Set the window parms - titles, location, size(ability), etc.
##+########################################################################

wm title    . "Apps Toolchest"
wm iconname . "Apps"
wm geometry . +150+30
wm resizable . 0 0


##+###########################################################
## Create a font to use for text on the drawers.
##
## A variable width font, like that typically used on 'button'
## widgets, provide an appropriate/nice appearance.
##+###########################################################

set feFONT_button "-family {comic sans ms} -size -14 -weight bold -slant roman"

eval font create fontTEMP_drawer  $feFONT_button


##+#########################################################################
## Establish an image to use as a background on the drawers (canvas widgets).
##
## This image should be very wide and be trimmed to a little more than
## the width (in pixels) of the longest text string of all the text strings
## to be placed on the drawers.
##
## We will place the image on the canvas widgets and use the '-width'
## parameter on the canvas commands to 'clip' the background image on
## the drawers (canvas widgets).
##+########################################################################

# image create photo IMG4drawerBkgnd -file "${DIR_gif_files}/$gif_img_file"
# image create photo IMG4drawerBkgnd -file "./buttonBkgnd_glass_blue_A_1130x24.gif"
# image create photo IMG4drawerBkgnd -file "./buttonBkgnd_glossy_orange_1024x24.gif"
  image create photo IMG4drawerBkgnd -file "./button_reliefMap_eastTexas_628x26.gif"

##+##############################################################
## Get the height (in pixels) of the drawer-background image.
##
## This MAY be used for the height of the drawers (canvas widgets).
## Another drawer-height option: the text height for a given font.
##+##############################################################

set imgHeightPx  [image height IMG4drawerBkgnd]


##+########################################################################
## Set the border width to be used for each drawer (canvas widget). 
##+########################################################################

# set BDwidth_canvas 4
  set BDwidth_canvas 0


##+########################################################################
## Put the text strings to be put on the drawers (canvas widgets)
## in variables. 
##
## We changed variables
##   textSTRING1, textSTRING2, ...
## to array  variables ...
##   textSTRING(1), textSTRING(2), ...
##
## In a 'production' script, these vars could be set by reading lines
## of a 'chest definition' file, for each toolchest.
##+########################################################################

set textSTRING(1) "Seamonkey - web browser"
set textSTRING(2) "Thunderbird - email client"
set textSTRING(3) "Filezilla - FTP client"
set textSTRING(4) "mtpaint - image editor"
set textSTRING(5) "SciTE - text editor"
set textSTRING(6) "gnome-screenshot - image capture"
set textSTRING(7) "gnome-terminal - command-interpreter window"
set textSTRING(8) "gcalctool - calculator"

set Ndrawers 8


##+########################################################################
## Set the width of all the drawers (in pixels) --- according to the
## longest text string to be placed on the drawers (canvas widgets). 
##
## We use 'font measure' to determine the text widths.
## Note that the font used for the text is a factor in determining the width.
##
## We get the max-string-width and add about 10 pixels to set a drawer-width
## a little wider than the longest text string on the drawers.
##+########################################################################

set drawerCNT 1
set strMaxWidthPx 15

while { $drawerCNT <= $Ndrawers } {

   set strWidthPx [font measure fontTEMP_drawer "$textSTRING($drawerCNT)"]

   ## FOR TESTING:
   #  puts "strWidthPx = $strWidthPx  drawerCNT = $drawerCNT"
   #  puts "$textSTRING($drawerCNT)"
   #  puts ""

   if { $strWidthPx > $strMaxWidthPx } {
      set strMaxWidthPx $strWidthPx
   }

   set drawerCNT [expr $drawerCNT + 1]
}

set drawerWidthPx [expr $strMaxWidthPx + 10]


##+########################################################################
## Set the height (in pixels) of the text strings, using 'font metrics' with
## the '-linespace' parm. This MIGHT be used to set drawer (canvas) height.
##
## Note that the text height depends on the font used for the strings.
##+########################################################################

set maxTextHeightPx [font metrics fontTEMP_drawer -linespace] 

## FOR TESTING:
#  puts "maxTextHeightPx = $maxTextHeightPx"


##+########################################################################
## Set the height of the canvas drawers (in pixels) from either
## the background image height or the 'linespace' height of the text strings.
##+#########################################################################
## Text height is NOT USED at this time.
## For the height of the drawers (canvas widgets),
## we will use the height of the image chosen for the background of the
## buttons. But we might use text-height in the future --- to 'trim' the
## height of the canvas drawers.
##+#########################################################################

  set drawerHeightPx $imgHeightPx
# set drawerHeightPx $maxTextHeightPx


##+########################################################################
## Set the position in each canvas widget (relative to the top left of each
## canvas widget) at which the left side of each text string will be located.
##
## We adjust the x text offset according the the width of the
## border of the canvas --- so that the text does not lie on the border.
##+########################################################################

set xLocTextPx [expr $BDwidth_canvas + 6]

# set yLocTextPx [expr $BDwidth_canvas + ($drawerHeightPx / 2)]
  set yLocTextPx [expr $drawerHeightPx / 2]
# set yLocTextPx [expr $maxTextHeightPx / 2]
# set yLocTextPx 6


##+########################################################################
## Loop to make the toolchest drawers. Each pass through the loop
##  - Defines a canvas widget of appropriate width, height, border-width.
##  - Puts a background image on the canvas.
##  - Puts a text string on the canvas 'drawer'.
##  - Packs the canvas widget in the parent window.
##+########################################################################

set drawerCNT 1

while { $drawerCNT <= $Ndrawers } {

   canvas .can($drawerCNT) \
      -relief raised \
      -borderwidth $BDwidth_canvas \
      -height $drawerHeightPx \
      -width $drawerWidthPx

   .can($drawerCNT) create image 0 0  \
      -image IMG4drawerBkgnd \
      -anchor nw

   .can($drawerCNT) create text \
      $xLocTextPx $yLocTextPx \
      -anchor w \
      -font fontTEMP_drawer \
      -text "$textSTRING($drawerCNT)"

   pack .can($drawerCNT) \
            -side top \
           -anchor nw \
           -fill none \
           -expand 0

   set drawerCNT [expr $drawerCNT + 1]
}
## END OF 'while' LOOP to define-pack canvas widgets


##+########################################################
## Loop to set BINDINGS for the drawers (canvas widgets).
##
## We use a tk_dialog popup to verify that the
## binding works as desired.
##+########################################################

set drawerCNT 1

while { $drawerCNT <= $Ndrawers } {

   set drawerCMD "tk_dialog .dialog1 \
          {Dear user:} {Toolchest drawer $drawerCNT was clicked} info 0 OK"

   ## FOR TESTING:
   #  puts "drawerCMD: $drawerCMD"

   bind .can($drawerCNT) <ButtonRelease-1> "$drawerCMD"

   set drawerCNT [expr $drawerCNT + 1]
}
## END OF 'while' LOOP to set BINDINGS on the canvas widgets


##+##################################
## Define PROCS: none
##+##################################


##+##################################
## Additonal GUI INITIALIZATION: none
##+##################################


 CODE FOR THE ONE-BACKGROUND-IMAGE-PER-ENTIRE-TOOLCHEST toolchest :
#!/usr/bin/wish -f
##
## SCRIPT: make_chest_proto_imageOnOneCanvas_textLineTagBindings.tk
##
##+##############################################################################
## DESCRIPTION: This Tk script
##
##    MAKES TOOLCHEST DRAWERS WITH: text lines, each with a tag for a binding
##    PROVIDES DRAWER ACTIONS WITH: button1-release bindings on the text tags
##    PROVIDES TOOLCHEST/DRAWER DECORATION WITH: 'image create' for a background
##                                                image on the one big canvas widget.
##                A rectangular GIF image file is used to make an image var
##                with 'image create photo'. That image var is used in the
##                'image create' statement to put the background on the one canvas.
##    
##    This script defines a single canvas widget, to contain one big background image
##    and the lines of text.
##
##    This script puts the text strings for the toolchest drawers in an array variable.
##    Then, in a loop over the array argument (drawer count), the text lines are
##    placed on the one big canvas, with a unique tag for each text line.
##
##    In the loop over the text-array argument, the action-bindings to each text line
##    are defined.
##
##+#############################################################################
## Created: 2012aug09 
##+#############################################################################

##+########################################################################
## Set the window parms - titles, location, size(ability), etc.
##+########################################################################

wm title    . "Apps Toolchest"
wm iconname . "Apps"
wm geometry . +150+30
wm resizable . 0 0


##+###########################################################
## Create a font to use for text on the drawers.
##
## A variable width font, like that typically used on 'button'
## widgets, provide an appropriate/nice appearance.
##+###########################################################

set feFONT_button "-family {comic sans ms} -size -14 -weight bold -slant roman"

eval font create fontTEMP_drawer  $feFONT_button


##+#########################################################################
## Establish an image to use as one big background on the one canvas.
##
## This image should be very wide and be trimmed to a little more than
## the width (in pixels) of the longest text string of all the text strings
## to be placed on the drawers.
##
## We will place the image on the one canvas widget and use the '-width'
## parameter on the canvas command to 'clip' the background image on
## the canvas.
##
## To avoid having a 'too plain' image after the clipping, which typically
## results in a 'toolchest' about 200 pixels wide and about 200 to 800 pixels
## high, it is good to choose images that have some interesting changes
## about every 200 pixels.
##+########################################################################

# image create photo IMG4canvasBkgnd -file "${DIR_gif_files}/$gif_img_file"
# image create photo IMG4canvasBkgnd -file "./bigBkgnd_antelope_canyon_sandstone_760x757.gif"
# image create photo IMG4canvasBkgnd -file "./bigBkgnd_RainDrops_onWaterSurface_1280x565.gif"
# image create photo IMG4canvasBkgnd -file "./bigBkgnd_blueWaterAndBeach_1280x744.gif"
# image create photo IMG4canvasBkgnd -file "./bigBkgnd_darkSky_clouds_moon_1024x718.gif"
# image create photo IMG4canvasBkgnd -file "./bigBkgnd_waterdrops_blueBackground_832x768.gif"
  image create photo IMG4canvasBkgnd -file "./bigBkgnd_sunlight_thru_blue_1143x300.gif"
# image create photo IMG4canvasBkgnd -file "./bigBkgnd_lightRays_green_1600x627.gif"
# image create photo IMG4canvasBkgnd -file "./bigBkgnd_reliefMap_eastTexas_568x664.gif"


##+########################################################################
## Set a borderwith for the one big canvas.
##+########################################################################

# set BDwidth_canvas 4
  set BDwidth_canvas 0


##+########################################################################
## Put the text strings to be put on the drawers (canvas widgets)
## in variables. 
##
## Instead of using variables
##   textSTRING1, textSTRING2, ...
## we use array  variables
##   textSTRING(1), textSTRING(2), ...
##
## In a 'production' script, these vars could be set by reading lines
## of a 'chest definition' file, for each toolchest.
##+########################################################################

set textSTRING(1) "Seamonkey - web browser"
set textSTRING(2) "Thunderbird - email client"
set textSTRING(3) "Filezilla - FTP client"
set textSTRING(4) "mtpaint - image editor"
set textSTRING(5) "SciTE - text editor"
set textSTRING(6) "gnome-screenshot - image capture"
set textSTRING(7) "gnome-terminal - command-interpreter window"
set textSTRING(8) "gcalctool - calculator"

set Ndrawers 8


##+########################################################################
## Set the width of all the drawers (in pixels) --- according to the
## longest text string to be placed on the toolchest 'drawers'. 
##
## We use 'font measure' to determine the text widths.
## Note that the font used for the text is a factor in determining the width.
##
## We get the max-string-width and add about 10 pixels to set a drawer-width
## a little wider than the longest text string on the drawers.
##+########################################################################

set drawerCNT 1
set strMaxWidthPx 15

while { $drawerCNT <= $Ndrawers } {

   set strWidthPx [font measure fontTEMP_drawer "$textSTRING($drawerCNT)"]

   ## FOR TESTING:
   #  puts "strWidthPx = $strWidthPx  drawerCNT = $drawerCNT"
   #  puts "$textSTRING($drawerCNT)"
   #  puts ""

   if { $strWidthPx > $strMaxWidthPx } {
      set strMaxWidthPx $strWidthPx
   }

   set drawerCNT [expr $drawerCNT + 1]
}

set drawerWidthPx [expr $strMaxWidthPx + 10]


##+########################################################################
## Set the height (in pixels) of the text strings, using 'font metrics' with
## the '-linespace' parm. This is to be used to set 'drawer' height.
##
## Note that the text height depends on the font used for the strings.
##+########################################################################

set maxTextHeightPx [font metrics fontTEMP_drawer -linespace] 

## FOR TESTING:
#  puts "maxTextHeightPx = $maxTextHeightPx"


##+########################################################################
## Set the height of the toolchest drawers (in pixels) from either
## the 'linespace' height of the text strings OR perhaps some other measure.
##+#########################################################################

set drawerHeightPx $maxTextHeightPx

## OR?
# set drawerHeightPx ???


##+########################################################################
## Set the x-offset in the one big canvas widget (relative to the left side
## of the canvas widget) at which the left side of each text string will be
## located.
##
## We adjust the x text offset according the the width of the
## border of the canvas --- so that the text does not lie on the border.
##+########################################################################

set xLocTextPx [expr $BDwidth_canvas + 6]


##+########################################################################
## Initialize the y-offset in the one big canvas widget (relative to the
## top of the canvas widget) at which the mid-left side of each text string
## will be located.
##+########################################################################

set yLocTextPx [expr $BDwidth_canvas + ($drawerHeightPx / 2)]


##+########################################################################
## - Define the one big canvas widget. 
## - Put the background image on it with 'image create'.
## - Pack the canvas widget.
##+########################################################################

set canvasHeightPx [expr $Ndrawers * $drawerHeightPx]

canvas .can \
      -relief raised \
      -borderwidth $BDwidth_canvas \
      -height $canvasHeightPx \
      -width $drawerWidthPx

## Put the image on the canvas.

.can create image 0 0  \
      -image IMG4canvasBkgnd \
      -anchor nw

pack .can \
            -side top \
           -anchor nw \
           -fill none \
           -expand 0


##+########################################################################
## Loop to make the toolchest 'drawers'  --- the lines of text on the canvas.
## 
## Each text line (drawer) is make with 'create text' on the one big canvas. 
##
## A unique tag is put on each text-line, for defining action bindings.
##
## A bind command is issued for each drawer, for button1-release action.
##
## 'create line' is used to make a separator line between each drawer.
##
## We use a tk_dialog popup to verify that the bindings work as desired.
##+########################################################################

set drawerCNT 1

while { $drawerCNT <= $Ndrawers } {

   ## Draw a separator line above each text line except the first one.

   if { $drawerCNT > 1 } {

      set yLineLocPx [ expr ( $drawerCNT - 1 ) * $drawerHeightPx ]

      .can create line \
         0 $yLineLocPx \
         $drawerWidthPx $yLineLocPx \
         -fill "#a0a0a0"

         # "#c0c0c0" - bright gray line (too faint?)
         # "#969696" - darker gray line (too dark?)
   }


   ## Put the text line on the canvas, with a tag.

   .can create text \
      $xLocTextPx $yLocTextPx \
      -anchor w \
      -font fontTEMP_drawer \
      -text "$textSTRING($drawerCNT)" \
      -tag textlineTag($drawerCNT)


   ## Bind an action to the text line.

   .can bind textlineTag($drawerCNT)  <ButtonRelease-1>   " tk_dialog .dialog1 \
      \"Dear user:\" \"Button $drawerCNT was clicked\" info 0 OK "


   ## Get ready for the next text line.

   set drawerCNT [expr $drawerCNT + 1]

   set yLocTextPx [ expr $yLocTextPx + $drawerHeightPx]

}
## END OF 'while' LOOP to define text-line 'drawers', with their bindings


##+#########################################################################
## BINDINGS:
## See the single '.can bind textlineTag($drawerCNT)  <ButtonRelease-1> ...'
## statement in the 'drawer' making loop above.
## Thus there is one bind command for each drawer.
##+#########################################################################

##+#########################################################################
## PROCS: none
##+#########################################################################

##+##################################
## Additonal GUI INITIALIZATION: none
##+##################################


Now I have plenty to do over the next year or two. I plan to integrate a couple of these toolchest making techniques into the 'feAppMenus' and 'feHandyTools' subsystems of my Freedom Environment software [2 ].

If anyone tries out either of the two 'image-on-canvas' scripts above (one-canvas-per-drawer OR one-canvas-per-toolchest) and comes up with some pretty appealing background images (or additional embellishment techniques/code), perhaps we can start a gallery page showing some particularly well-embellished toolchests.

Here is an image that shows that the backgrounds of the drawers do not have to be just vertical or horizontal color gradients. The background for these drawers was cropped from a color relief map of east Texas.

make_toolchest_prototype_reliefMapImgOnCanvases_screenshot_367x259.png

However, this image also shows that it may not be so easy to find pleasing backgrounds for the drawers. So far, I have found that simple, smooth color gradients provide the most pleasing backgrounds.