Version 6 of A color-gradient-button-maker GUI with 6 'miniscale' widgets

Updated 2012-09-07 00:30:57 by uniquename

uniquename - 2012sep06

On the page A color-gradient-button-maker GUI (with entry widget) and page A color-gradient-button-maker GUI with 6 spinboxes and page A color-gradient-button-maker GUI with 6 scale widgets, I presented code that can be used to make small or large rectangular image files (like GIF or PNG files) that contain a color gradient, given 2 colors.

Those GUI's are based on a 'DrawGradient' proc at Drawing Gradients on a Canvas which uses 'create line' commands on a 'canvas' widget to generate the color gradients --- with either horizontal or vertical lines.

As I pointed out on the A color-gradient-button-maker GUI with 6 spinboxes page, the re-drawing of the color-gradient, for a change in any of the 6 RGB color parameters involved, cannot be peformed as quickly as I would like --- in using either the entry-widget method or the 6-spinboxes method to provide for changing the 6 RGB values for the 2 colors.

As I pointed out on the A color-gradient-button-maker GUI with 6 spinboxes page, I should be able to experiment with color parameter changes much more rapidly if I use 6 'scale' widgets, instead of 6 'spinbox' widgets.

The main drawback is that the GUI will have to be larger, to accomodate 6 'scale' widgets, instead of 6 'spinbox' widgets.

Well after getting some experience with making a 'minilistbox' widget for the GUI on the A two-color rounded-POLYGON-maker GUI (equilateral and not so equilateral) page, I figured I should be able to make a 'miniscale' widget that takes less space than the typical 'scale' widget.

Here is an image of the resulting 6-miniscales GUI.

colorGradient_6miniscaleWidgets_screenshot_542x234.jpg

The 6-miniscales GUI takes less screen space than the 6-scales GUI. (See an image of the 6-scales GUI on the A color-gradient-button-maker GUI with 6 scale widgets page.)

I have used some space on this GUI with a label that explains how to use the Ctrl and Shift keys to speed up the change of the numbers in the 'miniscale' widgets. But I could add a 'ToggleHelp' button to the GUI (beside the Exit button). Clicking on the 'ToggleHelp' button would show/hide a scrollable-text-frame to shows the info on using the 'miniscale' widget.

___________________________________________________________

I present the code for the 6-miniscale GUI below. But first, here are some speed-of-gradient-drawing considerations.

Since the 'DrawGradient' proc takes about half a second to redraw the color-gradient in the canvas, I probably would get a rather 'jumpy' re-drawing of the color-gradient canvas if I tried 'binding' the redraw to ANY small change in one of the 6 RGB values, as a 'miniscale' widget is used to change an RGB value.

Like with the 6-spinbox implementation, I realized that if I triggered the re-draw on a mouse-button RELEASE, I could get a pretty pleasing color change on the GUI --- while allowing me to quickly change any of the 6 RGB color values.

And, unlike the 6-spinbox implementation, I can rapidly make BIG changes in any of the 6 RGB colors --- by using the Ctrl and Shift keys with mouse-button-1 on the 'miniscale' '+' and '-' button widgets.

This seems much more satisfying than watching a 'spinbox' widget relatively slowly roll numbers from one color setting to a quite different setting.

Furthermore, using the Shift key with mouse-button-1 pressed on the plus or minus buttons of the 'miniscale' widget is faster than changing an entry in a 'spinbox' with the keyboard.

So now I can experiment quickly with redrawing the color gradient by mouse-button press-and-hold and mouse-button release (rather than keying numbers in an entry field). The redraw of the color gradient can be done within a second or two, even for a big color change in any of the 6 RGB values.

Furthermore, the 6-'miniscales' take less space on the GUI than 6 'scale' widgets.

_________________________________________________________________

I follow my usual 'canonical' structure for Tk code, for this Tk script:

  0) Set general window & widget parms (win-name, win-position,
     win-color-scheme, fonts, widget-geometry-parms, win-size-control).

  1a) Define ALL frames (and sub-frames).

  1b) Pack ALL the frames.

  2) Define & pack all widgets in the frames.

  3) Define keyboard or mouse action BINDINGS, if needed.

  4) Define PROCS, if needed.

  5) Additional GUI initialization (typically with one or more of
     the procs), if needed.

This makes it easy for me to find code sections --- while generating and testing this script, and when looking for code snippets to include in other scripts.

To make the 'miniscale' widget to be used in step (2), I essentially inserted a new step:

1c) Define any procs to be used in making widgets.

You will find a 'miniscale' proc in that section.

_________________________________________________________________

Like with the 'entry widget' version and the '6-spinboxes' version and the '6-scales' version of this utility, I have put the four main pack parameters --- '-side', '-anchor', '-fill', '-expand' --- on the pack command for the various frames and widgets. So 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.

And if you want to see what will happen if you try letting the color-gradient-canvas be redrawn as any of the 'miniscale' widgets is rapidly changing the numbers in its variable, you can enhance the 'miniscale' proc to take a proc as an argument --- so that a redraw proc can be passed to each call to the 'miniscale' proc (which defines a 'miniscale' widget).

Note that there are <ButtonPress-1> bindings within the 'miniscale' proc (for the '+' and '-' buttons) --- and there are <ButtonRelease-1> bindings in the BINDINGS section of this code, for those same buttons.

The <ButtonPress-1> bindings increment/decrement the numbers (more or less rapidly). The <ButtonRelease-1> bindings call on the 'DrawGradient' proc.

#!/usr/bin/wish -f
##
## SCRIPT: make_gradient-on-canvas_6miniscales-2radiobuttons.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 GIF or 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 bottom frames of the GUI window:
##              - one frame for  2 radiobuttons and an 'Exit' button.
##              - 1 frame for 6 'miniscale' widgets.         
##
##           The 2 radiobuttons are to set the direction of the gradient ---
##           in the x direction or the y direction.
##
##           The 6 'miniscale' widgets are to set 2 pairs of RGB values --- of
##           the form  r1 g1 b1 r2 g2 b2 --- for the left-color (or top-color)
##           and right-color (bottom-color) of the gradient.
##
##           Examples of 2 settings of the radiobuttons and the 6 miniscales:
##             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.
##
##           There could be a 'command' parameter on the miniscale widget
##           which could be used to call the 'DrawGradient' proc to redraw
##           the color-gradient in the canvas as any miniscale (slider) changes.
##
##           However, the 'DrawGradient' proc is (probably) not fast enough
##           to re-draw all the gradient-lines in a smooth fashion as the
##           miniscale values are changed.
##
##           So we use a button1-release binding on each miniscale widget
##           to trigger a call to the 'DrawGradient' proc. By the time
##           the user mouse-releases one miniscale and goes to
##           move-and-release another miniscale, the 'DrawGradient' proc
##           can complete the re-draw.
##
##           Thus we get a 'semi-dynamic' (not quite immediate) GUI-update
##           action from the miniscales, rather than a fully 'dynamic'
##           (immediate) GUI-update action.
##
##           However, the user/programmer can experiment by commenting
##           out the 12 button1-release 'bind ' statements, and
##           implementing a 'command' parm for the 'miniscale' widget.
##           Then the 'DrawGradient' proc could be passed to the 6 miniscale
##           definition statements (calls to the 'miniscale' proc).
##
## REFERENCE:
## The 'DrawGradient' proc is based on a Tcl-Tk script by B. Oakley and
## Damon Courtney --- published at http://wiki.tcl.tk/6100 -
## 'Drawing Gradients on a Canvas'.  (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.
##+######################################################################
## 'CANONICAL' 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.
##
## The structure detail for this particular script:
##
##  1a) Define ALL frames:
## 
##      Top-level : '.fRcan' and '.fRbuttons' and  '.fRminiscales'
##
##      Sub-frames: none
##
##  1b) Pack ALL frames.
##
##  2) Define all widgets in the frames (and pack them):
##
##       - In '.fRcan': one 'canvas' widget 
##
##       - In '.fRbuttons': 1 button widget ('Exit') and
##                                   2 radiobuttons (for x or y),
##
##       - In the '.fRminiscales' frame:  6 miniscale widgets (with a label widget)
##
##  3) Define bindings:  2 x six, for the scale widget + and - buttons
##
##  4) Define procs:
##     - 'DrawGradient'    invoked by the 6 bindings
##
##  5) Additional GUI initialization:  Execute 'DrawGradient' once with
##                                     an initial, example set of 7 parms
##                                     --- x/y r1 g1 b1 r2 g2 b2 --- to start
##                                     with a color-gradient in the canvas
##                                     rather than a blank canvas.
##+########################################################################
## DEVELOPED WITH:
##   Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october, 'Karmic Koala).
##
##   $ wish
##   % puts "$tcl_version $tk_version"
##                                  showed   8.5 8.5   on Ubuntu 9.10.
##+#######################################################################
## MAINTENANCE HISTORY:
## Created by: Blaise Montandon 2012aug10
## Changed by: ...... ......... 2012
##+#######################################################################


##+#######################################################################
## Set general window parms (win-title, win-position, color-scheme, fonts,
## widget-geometry-parms, win-size-control).
##+#######################################################################

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

wm geometry . +15+30



##+######################################################
## Set the color scheme for the window and its widgets ---
## radiobuttons and spinboxes.
##+######################################################

tk_setPalette "#e0e0e0"

set radbuttBKGD "#ffffff"


##+########################################################
## Use a variable-width font for labels for the 6 spinboxes
## and the 2 radiobuttons and the Exit button text.
## Use a    fixed-width font for the spinbox entry field.
##+########################################################

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

font create fontTEMP_SMALL_varwidth \
   -family {comic sans ms} \
   -size -12 \
   -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_fixedwidth  \
   -family {liberation mono} \
   -size -14 \
   -weight bold \
   -slant roman

font create fontTEMP_SMALL_fixedwidth  \
   -family {liberation mono} \
   -size -12 \
   -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, and padding for Buttons)
##+###########################################################

set initCanWidthPx 300
set initCanHeightPx 24
set minCanHeightPx 24

# set BDwidthPx_canvas 2
  set BDwidthPx_canvas 0


## For buttons:
set PADXpx_button 0
set PADYpx_button 0
set BDwidthPx_button 2

## For labels:
set PADXpx_label 0
set PADYpx_label 0
set BDwidthPx_label 2



##+######################################################
## Set a minsize of the window according to the
## approx max WIDTH of the chars in the 'fRminiscales' frame
## --- 2 labels and 6 'miniscale' widgets.
##
## --- and according to the approx HEIGHT of the 4 frames
## --- 'fRcan' , 'fRbuttons', 'fRminiscales', 'fRinfo'.
##+######################################################

set minWinWidthPx [font measure fontTEMP_varwidth \
   "RGB-left :     RGB-right :"]

set minWinWidthPx [expr $minWinWidthPx + \
   [font measure fontTEMP_SMALL_fixedwidth \
       "1234 1234 1234 1234 1234 1234"]]

## Add some to account for right-left-size window/widget
## border-widths --- about 6 x 8 pixels/miniscale-widget.
set minWinWidthPx [expr 48 + $minWinWidthPx]


## Allow 1 char high for 'fRcan' ,
##       1 char high for 'fRbuttons',
##       2 chars high for 'fRminiscales',
##       3 chars high for 'fRinfo'

set minCharHeightPx [font metrics fontTEMP_SMALL_fixedwidth -linespace]

## and add 1 for the radiobuttons frame.
set minWinHeightPx [expr 7 * $minCharHeightPx]

## Add some to account for top-bottom window decoration (about 20 pixels)
## and frame/widget padding (about 8 pixels/line, 7 lines).
set minWinHeightPx [expr 56 + $minWinHeightPx]

## FOR TESTING:
#   puts "minWinWidthPx = $minWinWidthPx"
#   puts "minWinHeightPx = $minWinHeightPx"

wm minsize . $minWinWidthPx $minWinHeightPx


## 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-release any of the miniscales (or radiobuttons)
## 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


##+################################################################
## DEFINE *ALL* THE FRAMES:
##
##   Top-level : 'fRcan' , 'fRbuttons', 'fRminiscales', 'fRinfo'
##
##   Sub-frames: none
##+################################################################

# set BDwidth_frame 0
  set BDwidth_frame 2

frame .fRcan         -relief raised  -borderwidth $BDwidth_frame

frame .fRbuttons     -relief raised  -borderwidth $BDwidth_frame

frame .fRminiscales  -relief flat    -borderwidth $BDwidth_frame

frame .fRinfo        -relief raised  -borderwidth $BDwidth_frame


##+##############################
## PACK the 4 top-level FRAMES. 
##+##############################

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

pack .fRbuttons \
     .fRminiscales \
     .fRinfo \
      -side top \
      -anchor nw \
      -fill x \
      -expand 0


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

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

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



##+#########################
## DEFINE PROC 'miniscale'
## (for use in making a couple of demo widgets below)
##+##############################################################
## By using font and widget-geometry global variables 
##          - fontTEMP_SMALL_varwidth
##          - PADXpx_button
##          - PADYpx_button
##          - BDwidthPx_button
##          - BDwidthPx_label
## for the decorative & geometric elements/parameters of the GUI,
## we keep the arguments of this widget-made-on-the-fly down
## to 8 MAIN ELEMENTS/VARIABLES:
##
## - the parent widget/window --- a (sub)frame widget,
##
## - the name of the variable that is to hold the current scale value,
##
## - the min value of the range of the scale,
## - the max value of the range of the scale,
##
## - an initial value at which to initially set the scale variable value
##   --- and show in the 'label' widget of this 'miniscale' widget,
##
## - a 'resolution' of the scale, such as 1 or 5 or 0.1 or 0.25 or 0.002.
##   Each click on the '+' or '-' button of the 'miniscale' widget increases
##   or decreases the variable associated with this 'miniscale' widget,
##   by an amount equal to this resolution value.
##
## - a non-fractional digits specification to allow space for the digits
##   to the left of a decimal point --- which can keep the right-most digit
##   in the same place, instead of shifting left/right as the magnitude
##   of the scale value decreases/increases.
##   Example: 3 to allow for values 0 to 255 to stay justified right.
##            and 4 to allow values -1.0 to +1.0 in tenths to stay justified.
##
## - a fractional digits specification to make sure arithmetic precision errors
##   do not result in numbers like  0.30000000000000004 instead of 0.3.
##   Example values: 0 for integers and 1 for tenths.
##+##############################################################

proc miniscale {w scalevar minval maxval initval unit nonfracdigits fracdigits milsecs} {

   global fontTEMP_SMALL_varwidth fontTEMP_varwidth \
          PADXpx_button PADYpx_button \
          BDwidthPx_button BDwidthPx_label

   ##+###########################################
   ## DEFINE-and-PACK the widget SUB-FRAMES:
   ## '.frup-down' for 2 up and down buttons and
   ## '.fRlabel' to show the current scale value.
   ## Pack them side by side.
   ##+###########################################

   frame $w.fRlabelVAL -relief flat -bd 0
   frame $w.fRup-down  -relief flat -bd 0

   pack $w.fRlabelVAL \
        $w.fRup-down \
      -side left \
      -anchor w \
      -fill y \
      -expand 0


   ##+####################################################
   ## Initialize the 'miniscale' variable.
   #######################################################

   set $scalevar $initval

   ## FOR TESTING:
   #   puts "$scalevar : [set $scalevar]"

   ##+####################################################
   ## In FRAME '.fRlabel',
   ## DEFINE-and-PACK a label widget to show the current
   ## 'miniscale' widget value.
   ##+####################################################

   label $w.fRlabelVAL.labelVAL \
      -text "
$initval" \
      -font fontTEMP_SMALL_varwidth \
      -justify right \
      -anchor e \
      -width [expr $nonfracdigits + $fracdigits] \
      -relief flat \
      -fg red \
      -bd $BDwidthPx_label

   pack $w.fRlabelVAL.labelVAL \
      -side top \
      -anchor n \
      -fill none \
      -expand 0


   ##+####################################################
   ## In FRAME '.fRup-down',
   ## DEFINE-and-PACK a top-spacer label and 2 buttons.
   ##+####################################################

   button $w.fRup-down.buttUP \
      -text "+" \
      -font fontTEMP_SMALL_varwidth \
      -width 1 -height 1 \
      -pady 0 \
      -padx 0 \
      -command ""

#      -command "increment_miniscale $w $scalevar $minval $maxval $unit $nonfracdigits $fracdigits $milsecs"

   button $w.fRup-down.buttDOWN \
      -text "-" \
      -width 1  -height 1 \
      -font fontTEMP_SMALL_varwidth \
      -pady 0 \
      -padx 0 \
      -command ""


#      -command "decrement_miniscale $w $scalevar $minval $maxval $unit $nonfracdigits $fracdigits $milsecs"

   pack $w.fRup-down.buttUP \
        $w.fRup-down.buttDOWN \
      -side top \
      -anchor n \
      -fill none \
      -expand 0


   ##+#####################################################
   ## SET BINDING on the buttons in this new-widget so that
   ##         <ButtonPress-1> increments/decrements the
   ##         scalevar rapidly as the button continues to
   ##         be pressed.
   ##+#####################################################

   bind  $w.fRup-down.buttUP  <ButtonPress-1> \
         "increment_miniscale $w $scalevar $minval $maxval $unit $nonfracdigits $fracdigits [expr 16 * $milsecs]"

   bind  $w.fRup-down.buttUP  <Control-ButtonPress-1> \
         "increment_miniscale $w $scalevar $minval $maxval $unit $nonfracdigits $fracdigits [expr 4 * $milsecs]"

   bind  $w.fRup-down.buttUP  <Shift-ButtonPress-1> \
         "increment_miniscale $w $scalevar $minval $maxval $unit $nonfracdigits $fracdigits $milsecs"

   bind  $w.fRup-down.buttDOWN  <ButtonPress-1> \
         "decrement_miniscale $w $scalevar $minval $maxval $unit $nonfracdigits $fracdigits [expr 16 * $milsecs]"

   bind  $w.fRup-down.buttDOWN  <Control-ButtonPress-1> \
         "decrement_miniscale $w $scalevar $minval $maxval $unit $nonfracdigits $fracdigits [expr 4 * $milsecs]"

   bind  $w.fRup-down.buttDOWN  <Shift-ButtonPress-1> \
         "decrement_miniscale $w $scalevar $minval $maxval $unit $nonfracdigits $fracdigits $milsecs"

   bind  $w.fRup-down.buttUP  <ButtonRelease-1> \
         "set STOPvar 1"

   bind  $w.fRup-down.buttDOWN  <ButtonRelease-1> \
        "set STOPvar 1"

   ## Initialize STOPvar.
   global STOPvar
   set STOPvar 0

   ##+########################################################
   ## PROC 'increment_miniscale' -
   ##
   ## CALLED BY: 'ButtonPress-1' binding on the '+' button.
   ##+########################################################

   proc increment_miniscale {w scalevar minval maxval unit nonfracdigits fracdigits milsecs} {

      ## This 'upvar' associates the local var 'cur_scale_var' with
      ## the outer var 'scalevar' that is to contain the scale value.
      ## It is like an EQUIVALENCE statement in FORTRAN.

      upvar #0 $scalevar cur_scale_val

      global STOPvar
 
      while { $STOPvar != 1} {

         ## FOR TESTING:
         #   puts "cur_scale_val: $cur_scale_val"

         set cur_scale_val [expr  $cur_scale_val + $unit]

         if { $cur_scale_val > $maxval} {
            set cur_scale_val $maxval
         }

         set cur_scale_val  [format %${nonfracdigits}.${fracdigits}f  $cur_scale_val]

         $w.fRlabelVAL.labelVAL configure -text "
$cur_scale_val"

         ## Slow down the change of the scale to human reaction time levels.
         after $milsecs

         ## 'update' is needed to check for a button-release that sets STOPvar to 1.
         update

         ## FOR TESTING:
         #   puts "cur_scale_val: $cur_scale_val"

      }
      ## END OF LOOP while { $STOPvar != 1}

      set STOPvar 0

      ## The following are not needed.
      # set $scalevar $cur_scale_val
      ## FOR TESTING:
      #    puts "$scalevar : [set $scalevar]"

   }
   ## END OF proc 'increment_miniscale'


   ##+########################################################
   ## PROC 'decrement_miniscale' -
   ##
   ## CALLED BY: 'ButtonPress-1' binding on the '-' button.
   ##+########################################################

   proc decrement_miniscale {w scalevar minval maxval unit nonfracdigits fracdigits milsecs} {

      ## This 'upvar' associates the local var 'cur_scale_var' with
      ## the outer var 'scalevar' that is to contain the scale value.
      ## It is like an EQUIVALENCE statement in FORTRAN.

      upvar #0 $scalevar cur_scale_val

      global STOPvar

      while { $STOPvar != 1} {

         ## FOR TESTING:
         #   puts "cur_scale_val: $cur_scale_val"

         set cur_scale_val [expr  $cur_scale_val - $unit]

         if { $cur_scale_val < $minval} {
            set cur_scale_val $minval
         }

         set cur_scale_val  [format %${nonfracdigits}.${fracdigits}f  $cur_scale_val]

         $w.fRlabelVAL.labelVAL configure -text "
$cur_scale_val"

         ## Slow down the change of the scale to human reaction time levels.
         after $milsecs

         ## 'update' is needed to check for a button-release that sets STOPvar to 1.
         update

         ## FOR TESTING:
         #   puts "cur_scale_val: $cur_scale_val"

      }
      ## END OF LOOP while { $STOPvar != 1}

      set STOPvar 0

      ## The following are not needed.
      # set $scalevar $cur_scale_val
      ## FOR TESTING:
      #    puts "$scalevar : [set $scalevar]"

   }
   ## END OF proc 'decrement_miniscale'


}
## END OF 'miniscale' PROC


##+#########################################################
## OK. Now we are ready to define the widgets in the frames.
##+#########################################################


##+################################################################
## IN THE '.fRbuttons' frame -- DEFINE the 'Exit' button --- also
## 2 radiobuttons (with a label button).
##+################################################################

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

## Define label and 2 radiobuttons:

label .fRbuttons.lab_radbutts \
      -text "  Gradient direction:" \
      -font fontTEMP_varwidth \
      -justify left \
      -anchor w \
      -relief flat \
      -bd $BDwidthPx_button


set curDIRECTION "x"

radiobutton  .fRbuttons.radbuttX \
      -text "x" \
      -font fontTEMP_varwidth \
      -anchor w \
      -variable curDIRECTION \
      -value "x" \
      -selectcolor "$radbuttBKGD" \
      -relief flat \
      -bd $BDwidthPx_button
   
radiobutton  .fRbuttons.radbuttY \
      -text "y" \
      -font fontTEMP_varwidth \
      -anchor w \
      -variable curDIRECTION \
      -value "y" \
      -selectcolor "$radbuttBKGD" \
      -relief flat \
      -bd $BDwidthPx_button


##+###########################################
## Pack the widgets in the 'fRbuttons1' frame.
##+###########################################

pack .fRbuttons.buttEXIT \
     .fRbuttons.lab_radbutts \
     .fRbuttons.radbuttX \
     .fRbuttons.radbuttY \
      -side left \
      -anchor w \
      -fill none \
      -expand 0


##+#################################################################
## IN THE '.fRminiscales' frame -- DEFINE A LABEL and
## 3 'miniscale' WIDGETS and another LABEL and
## 3 'miniscale' WIDGETS.
##+#################################################################
##
## NOTE: We define a frame here to hold each 'miniscale' widget ---
##       instead of in the define-and-pack-all-frames section above.
##       We define a frame here rather than above, because these
##       frames are actually the widget (at least, the container
##       of the widget).
##+#################################################################

label .fRminiscales.labelRGB1 \
   -text "\
RGB-left :" \
   -width 10 \
   -font fontTEMP_varwidth \
   -relief flat \
   -bd 2

## DEFINE the 'miniscale' widget for 'curR1'.

frame .fRminiscales.fRminiscaleR1 -relief raised -bd 2
set curR1 255
miniscale .fRminiscales.fRminiscaleR1 curR1 0 255 $curR1  1 3 0 10
#          w scalevar minval maxval initval unit nonfracdigit fracdigit milsecs


## DEFINE the 'miniscale' widget for 'curG1'.

frame .fRminiscales.fRminiscaleG1 -relief raised -bd 2
set curG1 255
miniscale .fRminiscales.fRminiscaleG1 curG1 0 255 $curG1  1 3 0 10
#          w scalevar minval maxval initval unit nonfracdigit fracdigit milsecs


## DEFINE the 'miniscale' widget for 'curB1'.

frame .fRminiscales.fRminiscaleB1 -relief raised -bd 2
set curB1 0
miniscale .fRminiscales.fRminiscaleB1 curB1 0 255 $curB1  1 3 0 10
#          w scalevar minval maxval initval unit nonfracdigit fracdigit milsecs



##+########################################################
## THE 2nd 3 RGB 'miniscale' widgets -- with LABEL:
##+########################################################


label .fRminiscales.labelRGB2 \
   -text "\
    RGB-right :" \
   -width 12 \
   -font fontTEMP_varwidth \
   -relief flat \
   -bd 2

## DEFINE the 'miniscale' widget for 'curR2'.

frame .fRminiscales.fRminiscaleR2 -relief raised -bd 2
set curR2 255
miniscale .fRminiscales.fRminiscaleR2 curR2 0 255 $curR2  1 3 0 10
#          w scalevar minval maxval initval unit nonfracdigit fracdigit milsecs


## DEFINE the 'miniscale' widget for 'curG2'.

frame .fRminiscales.fRminiscaleG2 -relief raised -bd 2
set curG2 0
miniscale .fRminiscales.fRminiscaleG2 curG2 0 255 $curG2  1 3 0 10
#          w scalevar minval maxval initval unit nonfracdigit fracdigit milsecs


## DEFINE the 'miniscale' widget for 'curB2'.

frame .fRminiscales.fRminiscaleB2 -relief raised -bd 2
set curB2 0
miniscale .fRminiscales.fRminiscaleB2 curB2 0 255 $curB2  1 3 0 10
#          w scalevar minval maxval initval unit nonfracdigit fracdigit milsecs



##+##########################################################
## In the '.fRminiscales' FRAME -
## PACK the 2 LABELs & 6 'miniscale' widgets.
##+##########################################################

pack .fRminiscales.labelRGB1 \
     .fRminiscales.fRminiscaleR1 \
     .fRminiscales.fRminiscaleG1 \
     .fRminiscales.fRminiscaleB1 \
      -side left \
      -anchor w \
      -fill none \
      -expand 0

pack .fRminiscales.labelRGB2 \
     .fRminiscales.fRminiscaleR2 \
     .fRminiscales.fRminiscaleG2 \
     .fRminiscales.fRminiscaleB2 \
      -side left \
      -anchor w \
      -fill none \
      -expand 0


##+######################################################
## In the '.fRthree' FRAME-
## DEFINE-and-PACK a 'label' widget.
##+######################################################

label .fRinfo.labelINFO \
   -text "\
Mouse-button-1 down on the '+' or '-' buttons to change values.
 Press the 'Ctrl' key, then button-1, to change 4 times faster.
 Press the 'Shift' key, then button-1, for 16 times faster." \
   -font fontTEMP_SMALL_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd $BDwidthPx_label

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

##+#######################################################
## END OF DEFINING-and-PACKING ALL WIDGETS and
## END OF DEFINING the GUI.
##+#######################################################


##+#######################################################################
##  BINDINGS SECTION:
##        - one each, for button1-release on each of the 6 miniscales
##        - one each, for button1-release on each of the 2 radiobuttons
##+#######################################################################

if {1} {

bind .fRminiscales.fRminiscaleR1.fRup-down.buttUP <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
     .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}

bind .fRminiscales.fRminiscaleR1.fRup-down.buttDOWN <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
     .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}


bind .fRminiscales.fRminiscaleG1.fRup-down.buttUP <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}

bind .fRminiscales.fRminiscaleG1.fRup-down.buttDOWN <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}


bind .fRminiscales.fRminiscaleB1.fRup-down.buttUP <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}

bind .fRminiscales.fRminiscaleB1.fRup-down.buttDOWN <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}


bind .fRminiscales.fRminiscaleR2.fRup-down.buttUP <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}

bind .fRminiscales.fRminiscaleR2.fRup-down.buttDOWN <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}


bind .fRminiscales.fRminiscaleG2.fRup-down.buttUP <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}

bind .fRminiscales.fRminiscaleG2.fRup-down.buttDOWN <ButtonRelease-1>  {set STOPvar 1 ; DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}


bind .fRminiscales.fRminiscaleB2.fRup-down.buttUP <ButtonRelease-1>  {DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}

bind .fRminiscales.fRminiscaleB2.fRup-down.buttDOWN <ButtonRelease-1>  {DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}


## button1 release bindings  on 2 radiobuttons:

bind .fRbuttons.radbuttX <ButtonRelease-1>  {DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}

bind .fRbuttons.radbuttY <ButtonRelease-1>  {DrawGradient \
      .fRcan.can $curDIRECTION $curR1 $curG1 $curB1 $curR2 $curG2 $curB2}

}
## END OF 'if {1/0}' SECTION
## To easily disable the bindings.


##+######################################################################
##  PROCS SECTION: 
##    - 'DrawGradient'  to fill the specified canvas according to the
##                      7 parms --- x/y r1 g1 b1 r2 g2 b2
##+######################################################################

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.

DrawGradient .fRcan.can $curDIRECTION \
      $curR1 $curG1 $curB1 $curR2 $curG2 $curB2

Like I said at the bottom of the A color-gradient-button-maker GUI page:

Make one enhancement to a Tk script and it leads to other enhancements or completely new scripts. It never ends.

But, hopefully, this ends (at least for a few months --- or weeks?) my attempts to make a better color-gradient-maker GUI.

__________________

Thanks to Richard Suchenwirth for his 'spinner' widget demo on the spinbox page. That led me to make a similar 'minilistbox' widget --- and then to making this 'miniscale' widget --- using widgets ('label' and 'button') that have existed since Tcl-Tk 7.x. Hence the 'miniscale' widget should work for people with old versions of the 'wish' interpreter.