Version 2 of A color-gradient-button-maker GUI

Updated 2012-08-04 19:52:12 by uniquename

uniquename - 2012aug04

I tried out the code at Drawing Gradients on a Canvas and was surprised how fast it fills large rectangular canvas areas with a color gradient.

Apparently the trick is to use 'create line' rather than 'put at x y' --- i.e. make horizontal or vertical lines, rather than poking a color into each x,y location.

That drawing-gradients demo ties in with my wish to make nice backgrounds for buttons in application or utility 'toolchests', as indicated at my request to "Implement '-anchor' (something similar) for compound text on image" at the Tk 9.0 WishList page.

I have enhanced the code at Drawing Gradients on a Canvas to provide a GUI with an entry widget (at the bottom of the GUI) to enter 7 parameters of the form 'x/y r1 g1 b1 r2 g2 b2', along with a 'Draw' button --- to draw the specified gradient into the canvas widget at the top of the GUI.

Here is a sample image:

draw-color-gradient-GUI_screenshot_544x108.png

Note: I changed the draw-gradient proc at Drawing Gradients on a Canvas to use RGB values (in decimal form), rather than color names --- so that a much wider range of color choices is available.

Now I can quickly make color-gradient 'button files' by doing screen/window captures of this GUI --- with a screenshot utility, like 'gnome-screenshot' on Linux.

Below is the code for this 'gradient-button-maker' GUI. It includes plenty of comments to explain the GUI.

One thing to note is that I have put the four main pack parameters --- '-side', '-anchor', '-fill', '-expand' --- on the pack command for the various frames and widgets. This is so that Tcler's can experiment with these parameters if they want to change the behavior of the GUI when window size is changed.

Alternatively, Tcler's can activate the commented statement

wm resizeable . 0 0

to make the canvas a fixed size and avoid any confusion that might be caused by allowing the window to be resized.

#!/usr/bin/wish -f
##
## SCRIPT: make_gradient-button_rectangular.tk
##
##########################################################################
## PURPOSE:  This TkGUI script facilitates the creation of
##           rectangular color-gradient images that can be used, for example,
##           for the background of 'buttons' in GUIs such as 'toolchests'.
##
##           A screen/window capture utility (like 'gnome-screenshot' on Linux)
##           can be used to capture the image in a PNG file, say.
##
##           Then, if necessary, an image editor (like 'mtpaint' on Linux)
##           can be used to crop the window capture image to get only the
##           rectangular area of the canvas containing the color-gradient
##           --- or some sub-rectangle of that area.
##
##           Furthermore, utilities (such as the ImageMagick 'convert' command
##           on Linux) can be used to 'mirror' or 'flip' a gradient image in
##           an image file (PNG or JPEG or GIF). The 'mirror'  and 'flip'
##           operations can be applied vertically or horizontally ---  and
##           can be applied multiple times, for various visual effects.
##
##           The resulting rectangular color-gradient image can then be used as a
##           background in Tk widgets, such as button or canvas or label widgets
##           in 'toolchests' or other types of GUIs. 
##
## METHOD:   The GUI contains a rectangular canvas widget into which the
##           color gradient is drawn with canvas 'create line' commands,
##           where the lines can be either horizontal (in the x direction)
##           or vertical (in the y direction).
##
##           In addition to the canvas widget (in a top frame of the GUI window),
##           in a bottom frame of the GUI window, there are a couple of
##           buttons ('Draw' and 'Exit') and an entry field.
##
##           The entry field contains 7 values --- of the format
##               x/y r1 g1 b1 r2 g2 b2
##           Examples:
##             x 255 255 0 255 0 0
##             y 255 0 255 0 0 255
##
##           The first example says draw the lines horizontally starting
##           from yellow on the left to red on the right.
##
##           The second example says draw the lines vertically starting
##           from magenta at the top to blue on the bottom.
##
##           The seven parms (x/y r1 g1 b1 r2 g2 b2)
##           are passed into a 'DrawGradient' proc that draws the lines
##           within the canvas, filling the canvas with colored pixels.
##
###########################################################################
## REFERENCE:
## The 'DrawGradient' proc is based on a Tcl-Tk script by Damon Courtney
## --- published at http://wiki.tcl.tk/6100 .  (downloaded 2011sep26)
## That script draws gradients on multiple rectangular canvases, packed
## top to bottom. You need to edit that script to change colors or
## gradient direction. No GUI for entry of those indicators is provided.
##
###########################################################################
## STRUCTURE OF THIS CODE:
##
##  0) Set general window parms (name,position,[size,]color-scheme,fonts,
##     widget-geometry-parms,etc.).
##  1) Define ALL frames (and sub-frames).  Pack them.
##  2) Define all widgets in the frames. Pack them.
##
##  3) Define keyboard or mouse action BINDINGS, if needed.
##  4) Define PROCS, if needed.
##  5) Additional GUI INITIALIZATION (with procs), if needed.
##
## In more detail:
##
##  1a) Define ALL frames:
## 
##      Top-level : '.fRtop' and '.fRbottom'
##
##      Sub-frames: none
##
##  1b) Pack ALL frames.
##
##  2) Define all widgets in the frames (and pack them):
##
##       - In '.fRtop': one 'canvas' widget 
##
##       - In '.fRbottom': 2 button widgets ('Draw' and 'Exit') and
##                        an entry widget (for the 7 gradient-drawing parms)
##
##  3) Define bindings:  one, for the entry widget
##
##  4) Define procs:
##     - 'DrawGradient'    invoked by the 'Draw' button
##
##  5) Additional GUI initialization:  none
##                                    (or could execute 'DrawGradient'
##                                     once with an initial, example
##                                     set of 7 parms --- to start with
##                                     a color-gradient in the canvas
##                                     rather than a blank canvas)
##
##########################################################################
## DEVELOPED WITH:
##   Tcl-Tk 8.5  (or 8.4) on Ubuntu 9.10.
##
##   $ wish
##   % puts "$tcl_version $tk_version"
##                                  showed   8.4 8.4   or
##                                           8.5 8.5   on Ubuntu 9.10.
##
###########################################################################
## MAINTENANCE HISTORY:
## Created by: Blaise Montandon 2012aug01
## Changed by: ...... ......... 2012
###########################################################################

#########################################################################
## Set general window parms (title,position,size,color-scheme,fonts,etc.).
#########################################################################

wm title    . "Draw-Color-Gradient in a Rectangular Canvas"
wm iconname . "DrawGradient"

wm geometry . +15+30

## We allow the window to be resizable and we pack the canvas with
## '-fill both' so that the canvas can be enlarged by enlarging the
## window. Just click the 'Draw' button to re-fill the canvas with
## the user-specified color gradient.

## If you want to make the window un-resizable, 
## you can use the following statement.
# wm resizable . 0 0


## Set the color scheme for the window and its widgets.

tk_setPalette "#cfcfcf"


## Use a variable-width font for buttons (and labels).
## Use a    fixed-width font for the entry field.

font create fontTEMP_button \
   -family {comic sans ms} \
   -size -14 \
   -weight bold \
   -slant roman

## Some other possible (similar) variable width fonts:
##  Arial
##  Bitstream Vera Sans
##  DejaVu Sans
##  Droid Sans
##  FreeSans
##  Liberation Sans
##  Nimbus Sans L
##  Trebuchet MS
##  Verdana

font create fontTEMP_entry  \
   -family {liberation mono} \
   -size -14 \
   -weight bold \
   -slant roman

## Some other possible fixed width fonts (esp. on Linux):
##  Andale Mono
##  Bitstream Vera Sans Mono
##  Courier 10 Pitch
##  DejaVu Sans Mono
##  Droid Sans Mono
##  FreeMono
##  Nimbus Mono L
##  TlwgMono


##########################################################################
## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS.
## (e.g. width and height of canvas, padding for Buttons)
##########################################################################

set initCanWidthPx 400
set initCanHeightPx 24

set PADXpx_button 0
set PADYpx_button 0


set BDwidthPx_button 2

# set BDwidthPx_canvas 2
  set BDwidthPx_canvas 0

set BDwidthPx_entry 2

set initENTRYwidthChars 30


#######################################################################
## DEFINE *ALL* THE FRAMES:
##
##   Top-level : 'fRtop' and 'fRbottom'
##               
##   Sub-frames: none
#######################################################################

set BDwidth_frame 2

frame .fRtop     -relief raised  -borderwidth $BDwidth_frame

frame .fRbottom  -relief raised  -borderwidth $BDwidth_frame


###################################################################
## PACK the 2 top-level FRAMES. 
###################################################################

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

pack .fRbottom \
        -side top \
        -anchor nw \
        -fill x \
        -expand 0


################################
## DEFINE-and-PACK CANVAS WIDGET:
################################

canvas .fRtop.can \
   -width $initCanWidthPx \
   -height $initCanHeightPx \
   -relief raised \
   -borderwidth $BDwidthPx_canvas

pack .fRtop.can \
         -side top \
        -anchor nw \
        -fill both \
        -expand 1


##########################################################################
## IN THE '.fRbottom' frame -- DEFINE the 'Draw' and 'Exit' buttons
## --- and a pair of label and entry widgets.
##########################################################################
        
button .fRbottom.buttDRAW \
        -text "Draw" \
        -font fontTEMP_button \
        -padx $PADXpx_button \
        -pady $PADYpx_button \
   -relief raised \
         -bd $BDwidthPx_button \
        -command {eval DrawGradient .fRtop.can $ENTRYstring}


button .fRbottom.buttEXIT \
        -text "Exit" \
        -font fontTEMP_button \
        -padx $PADXpx_button \
        -pady $PADYpx_button \
   -relief raised \
         -bd $BDwidthPx_button \
        -command {exit}

label .fRbottom.lab \
        -text "\
Draw-Color-Gradient parms:
     (x/y r1 g1 b1 r2 g2 b2)" \
        -font fontTEMP_button \
        -justify left \
        -anchor w \
        -relief flat \
        -bd $BDwidthPx_button

set ENTRYstring "x 255 255 0 255 0 0"

entry .fRbottom.ent \
   -textvariable ENTRYstring \
   -bg "#f0f0f0" \
   -font fontTEMP_entry \
   -width $initENTRYwidthChars \
   -relief sunken \
   -bd $BDwidthPx_entry


#############################################
## Pack the widgets in the 'fRbottom' frame.
#############################################

pack .fRbottom.buttEXIT \
        -side left \
        -anchor w \
        -fill none \
        -expand 0

pack .fRbottom.buttDRAW \
        -side left \
        -anchor w \
        -fill none \
        -expand 0

pack .fRbottom.lab \
        -side left \
        -anchor w \
        -fill none \
        -expand 0

pack .fRbottom.ent \
        -side left \
        -anchor w \
        -fill x \
        -expand 1


##########################################################################
##  BINDINGS SECTION:  one, for Enter key in the entry field.
##########################################################################

bind .fRbottom.ent <Return>  {eval DrawGradient .fRtop.can $ENTRYstring}


##########################################################################
##  PROCS SECTION: 
##    - DrawGradient'   to fill the specified canvas according to the
##                      7 parms from the ENTRYstring variable
##########################################################################

proc DrawGradient {win axis r1 g1 b1 r2 g2 b2} {

   global ENTRYstring

   # 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 {
         # set ENTRYstring "$ENTRYstring ERR: Invalid 1st parm. Must be x or y."
         # return
         return -code error "Invalid 1st parm: $axis.  Must be x or y"
      }
   }

   if { $r1 > 255 || $r1 < 0 } {
      return -code error "Invalid color value for r1: $r1"
   }


   if { $g1 > 255 || $g1 < 0 } {
      return -code error "Invalid color value for g1: $g1"
   }

   if { $b1 > 255 || $b1 < 0 } {
      return -code error "Invalid color value for b1: $b1"
   }

   if { $r2 > 255 || $r2 < 0 } {
      return -code error "Invalid color value for r2: $r2"
   }

   if { $g2 > 255 || $g2 < 0 } {
      return -code error "Invalid color value for g2: $g2"
   }

   if { $b2 > 255 || $b2 < 0 } {
      return -code error "Invalid color value for 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 {%2.2x} $nR]
      append col [format {%2.2x} $nG]
      append col [format {%2.2x} $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}
      }
   }

   # return $win
}
## END OF proc DrawGradient


##################################################################
## Additional GUI initialization, if needed (or wanted).
##################################################################

update

## 'update' is needed before DrawGradient so that the
## canvas width and height are implemented.
## DrawGradient uses 'winfo' to get those dimensions.

eval DrawGradient .fRtop.can $ENTRYstring

Now I have a tool to make nice color-gradient buttons for my Freedom Environment toolchests (see [L1 ]) --- in future releases.

I may try using some of the ideas in the Functional imaging page (thanks, Suchenwirth, for your many contributions) to make more complex color gradients than simple x-direction or y-direction gradients. But it make take a careful weeding out of the many functions presented there, to find functions that provide nice backgrounds for toolchest buttons (nice subtle gradients that do not distract from the text on the 'toolchest drawer' buttons).

For now, I can use the ImageMagick commands 'convert' and 'composite' (as in the 'IMAGEtools' scripts of the 'feNautilusScripts' subsystem of the Freedom Environment software) to combine x and y gradient buttons (and other buttons) to make some additional types of gradient buttons.

Make one enhancement to a Tk script and it leads to other enhancements or ideas for completely new scripts. It never ends. Darn you, Ousterhout, for starting all this. :-)