Updated 2014-06-07 21:51:47 by uniquename

uniquename - 2012aug16

I am interested in making nice images for 'toolchest' and 'drawer' backgrounds, as I have indicated at Experiments in making embellished GUI's and at A color-gradient-button-maker GUI and at A two color rounded-rectangle-maker GUI.

After making the enhanced rounded-rectangle-maker Tk script, I decided to follow up on the wiki post called Drawing rounded polygons by Keith Vetter.

It is a generalization of the concept of putting rounded corners on rectangles. Vetter took some routines contributed by R. Suchenwirth, for making regular polygons and a 5-pointed star, and made a Tk script that draws the polygons with rounded corners.

It occurred to me that some solid-colored polygons like squares and pentagons and octagons could be used to make backgrounds for icons.

And I may soon tire of using rounded rectangles for wide buttons. Perhaps I could used 'flattened' pentagons for wide buttons.

In any case, I decided to take Vetter's script and provide some enhancements --- such as adding the option to specify 2 colors --- one for the interior of the polygon and one for the exterior (which can be done by specifying a color for the canvas).

Also, I noticed that on Vetter's GUI, he had radiobuttons for N-sided polygons where N goes from 3 to 10. But in the case of N=4, he had an option for either a square or a rectangle.

It turns out that the square does not turn out to be square --- except for one precise window size. It turns out that if you choose square, you ordinarily get a 'fat' rectangle. And if you choose rectangle, you get a thin, wide rectangle.

Well, it occurred to me that for all of the polygons, from 3 to 10, the user should have the option to specify that an EQUILATERAL polygon should be drawn. This could be asked for by a checkbutton on the GUI.

And if the checkbutton were not checked, the drawing routine would simply draw the polygon in an aspect ratio determined by the current aspect ratio of the canvas.

Some other enhancements occurred to me (especially after getting into the coding and testing of the new script), but adding the color and equilateral options was enough to get me started on making a Tk script for an enhanced 'rounded-polygon-making GUI'.

I came up with a script that produces a GUI which behaves very nicely as the window is resized. Here is an image of the GUI showing how it can be used to make a pentagon-shaped background for an icon. Note that the 'equilateral' checkbutton is checked.

With Vetter's script, the rounded polygon was always drawn with a dotted outline (and light gray area) showing the portion of the polygon corners/points that were being trimmed off.

I decided to make the dotted outline (and light gray area) an option by adding a 'Show vertices' checkbutton to the GUI. Here is an image of the GUI showing how it displays the sharp-corners of a polygon. Note that the 'Show vertices' checkbutton is checked.

Note that I have supplied 2 buttons on the GUI with which to set the 'inside' and 'outside' colors for the rounded polygon. Those two buttons call on a color-selector-GUI script to set those two colors. You can make that color-selector script by cutting-and-pasting the code from the page A non-obfuscated color selector GUI on this site.

(A set of 6 spinboxes or 6 scales could be put on the GUI to set the RGB values of the 2 colors --- but those widgets take a lot of space on the GUI. And using entry widgets is rather clutzy and does not facilitate quick changes of the colors.)

____________________________________________________________________________

In place of all the radiobuttons of Vetter's GUI, to specify the type of polygon to draw, I decided to use a 'spinbox' --- to conserve space on the GUI and to allow for adding many more N-gons in the future --- or special shapes, like the 5-pointed-star.

However, I did not want to use the 'spinbox' because I wanted even those users who have an old version of the 'wish' interpreter --- an old, pre-spinbox 8.x version --- or even an old 7.x version --- to be able to run this rounded-polygon utility.

In researching the spinbox, I ran across the spinbox page on this wiki --- and I saw the little example of a 'spinner' widget, by Richard Suchenwirth, which he said was "a concoction of a 1-line high listbox with two tiny buttons, to approximate the effects of a spinbox.".

After experimenting with his demo code and enhancing it somewhat (with font variables and width variables and other parameters), I was ready to build a 'minilistbox' widget-making proc into this rounded-polygon utility.

You can see the code for that proc in the code for the entire rounded-polygon-making Tk script, below. And you can see how the proc is used in this non-trivial use-case.

(I may extract the 'minilistbox' code and put it in a simpler demo script, like Suchenwirth did with his 'spinner' proc --- someday. For now, people who want to use this 'minilistbox' --- or make a similar widget --- can extract the proc and the calling code from this much larger script.)

____________________________________________________________________________

The features of this GUI are detailed in comments in the Tk script code for this utility (below).

There is a 'USING THE GENERATED IMAGE' section in the comments near the top of the code below that describe how a captured-cropped image from this GUI could be used to make
  - a SEMI-TRANSPARENT, SOLID-COLOR polygon with TRANSPARENT CORNERS

OR
  - a SEMI-TRANSPARENT, NON-SOLID-COLOR polygon (with optional
     TRANSPARENT CORNERS) --- by using the captured image as a 'mask'.

In any case, various types of rounded-polygon image files could be made from images generated by this utility --- SOLIDcolor-NONtransparent, SOLIDcolor-SEMItransparent, NONsolidColor-NONtransparent, and NONsolidColor-SEMItransparent.

And those images could be processed in many different ways (such as blur, to 'feather' edges --- or apply a rounded-bevel, to get a 3D effect --- for example, by using the ImageMagick 'convert' utility) to make buttons/drawers/icons/bullets/etc for 'toolchests' and other types of GUI's.

___________________________________________________________________________

As in all my other Tk scripts, I use a 'canonical' structuring of the code:
0) Set general window 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 and 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 two of the procs), if needed.

This structure is discussed in more detail on the page A Canonical Structure for Tk Code --- and variations.

In this particular script, I had to insert a new step in these steps, which have served me so well for about 10 years, in making about a hundred different Tk scripts.

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

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

_________________________________________________________________

Vetter's rounded-polygon-making script used the 'grid' geometry manager. I changed to using the 'pack' command.

As in all my scripts that use the 'pack' geometry manager (which is all of my scripts, so far), I provide the four main pack parameters --- '-side', '-anchor', '-fill', and '-expand' --- on all the 'pack' commands for the frames and widgets.

I think I have found a good setting of the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various widgets. In particular ...

The 'canvas' widget expands/contracts appropriately when the window size is changed --- and button and label widgets stay fixed in size and relative-location as the window size is changed.

One thing I did that some people might find disconcerting is that I have allowed the 'scale' widget (for 'dynamically' changing the radius of the corners of the polygons) to expand/contract in the x-direction as the window x-size is changed.

If anyone wants to change the way the GUI configures itself as the main window size is changed, they can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various widgets --- to get the widget behavior that they want.

 Code for the Tk script 'makePolygons_withRoundedCorners_2solidColors.tk' :

#!/usr/bin/wish -f
##
## SCRIPT: makePolygons_withRoundedCorners_2solidColors.tk
##
## 
## PURPOSE:  This Tk GUI script facilitates the creation of polygons
##           (3 to 10 sided ; i.e. triangle to decagon - also a star)
##           as solid-color images with rounded corners at vertices ---
##           on a canvas widget.
##
##           An image consists of one polygon done in two colors ---
##           one color inside the polygon (with round corners) and a
##           different color outside the rounded polygon (i.e. the
##           canvas background).
##
## METHOD:   The GUI contains a rectangular canvas widget on which the
##           solid-color polygon with rounded corners is drawn.
##
##           The GUI includes a 'scale' widget whose slider-bar can
##           be used to change the radius of the corners dynamically.
##           That is, the polygon (or star) corners change radius as
##           the slider-bar is dragged in either direction.
##
##           The GUI also includes the capability to set the 2 colors
##           of the image. (Any number of methods could be used to
##           set the two colors: two entry fields OR 2 buttons which
##           call on an external script to allow the user to set each
##           color via a color-selector-GUI. We use the latter.)
##
##           (A set of 6 spinboxes or 6 scales could be put on the GUI
##            to set the RGB values of the 2 colors --- but those widgets
##            take a lot of space on the GUI.)
##
##           There is a '-command' parameter tied to the radius-setting 'scale'
##           widget. That parameter is used to call a 'ReDraw' proc
##           to redraw the solid-colored polygon with round corners --- as
##           the slider-bar is used to change the value of a radius variable.
##
##           The redraw includes clearing the canvas and redrawing
##           polygons on the canvas for each detected change in radius.
##
##           Since erasing these items from the canvas and redrawing them
##           completes within a very small fraction of a second, it is
##           feasible to do the redraws 'dynamically' with the sliderbar.
##
## USING THE GENERATED IMAGE:
##           A screen/window capture utility (like 'gnome-screenshot'
##           on Linux) can be used to capture the GUI image in a GIF file, say.
##
##           If necessary, an image editor (like 'mtpaint' on Linux)
##           can be used to crop the window capture image.  The editor
##           could also be used to blur the image slightly to 'feather'
##           the edges of the polygon.
##
##           Several 'use-cases' for the captured-cropped image file:
##
##        1) The colored image could be used, directly, for the background of
##           'buttons'/'drawers'/'icons'/'bullets' in GUIs such as 'toolchests'.
##
##        2) The colored image file could be used with a utility (like the
##           ImageMagick 'convert' command) to change the outer (or inner)
##           color to TRANSPARENT, making a partially transparent GIF
##           (or PNG) file --- for example, with the 'rounded-off' corners
##           being part of the transparency.
##           Then the semi-transparent, solid-color image file could be used,
##           directly, for the background of 'buttons'/'drawers'/etc in GUIs
##           such as 'toolchests'.
##
##        3) The semi-transparent, solid-color image file from use-case 2
##           could be used as a MASK on a non-solid-color image file of
##           the same size.
##           A utility (like the ImageMagick 'convert' or 'composite' command)
##           could be used to 'apply' the mask to the NON-solid-color image
##           file, making a rounded-corner semi-transparent image file
##           (GIF or PNG) from the NON-solid-color image.
##           Then the semi-transparent, NON-solid-color image file could be
##           used, for 'buttons'/'drawers'/etc in GUI's.
##
##        4) For a family of GUI 'toolchests', the toolchests will generally
##           require a different width button/drawer for each toolchest.
##           In that case, the semi-transparent, NON-solid-color image file
##           from use-case 3 could be STRETCHED-OUT (by any one of several means)
##           to make a semi-transparent, NON-solid-color image of the required
##           width for each toolchest.
##
##+########################################################################
## REFERENCE:
##       http://wiki.tcl.tk/8590 - 'Rounded Polygons'
##       (code downloaded 2012aug14)
##       Author: KPV = Keith Vetter 2003
##
##+########################################################################
## 'CANONICAL' STRUCTURE OF THIS TK CODE:
##
##  0) Set general window parms (win-name,win-position,win-size-control,
##                 win-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.
##
##
## Some detail about the code structure of this particular script:
##
##  1a) Define ALL frames:
## 
##      Top-level :  '.fRbuttons' , '.fRpolyspecs' , '.fRcan'
##
##      Sub-frames: none
##
##  1b) Pack ALL frames.
##
##  1c) Define a 'minilistbox' proc that is used to make a couple of
##      COMPACT LIST-SELECTION WIDGETS for use in step 2 below --- to serve
##      in place of the old-fashioned 'tk_optionMenu' widget, and yet
##      to avoid using a newer widget like 'spinbox' that is
##      not available to users of older 8.x wish interpreters
##      or the really-old 7.x interpreters.
##
##  2) Define all widgets in the frames (and pack them):
##
##     - In '.fRbuttons':   1 button widget ('Exit'),
##                            and
##                          2 buttons (for setting the inside/outside colors),
##                            and
##                          1 checkbutton to ask for an EQUILATERAL n-gon
##                            and 
##                          1 checkbutton to ask that a DOTTED 'TRACE' of
##                            the non-rounded polygon 'corners' be shown
##                            'in the background' --- in light-gray fill,
##                            with a black dotted outline.
##
##     - In '.fRpolyspecs': 1 'minilistbox' widget to specify the polygon type,
##                            which is usually indicated by the number, N, of
##                            sides (but using 0 to request the star polygon),
##                            and
##                          1 'minilistbox' widget to specify a rotation
##                            angle to use in drawing the polygon (sets the
##                            place where the first polygon vertex is drawn)
##                            and
##                          1 radius 'scale' widget, to 'dynamically' change
##                            the radius of the polygon 'corners'
##
##     - In '.fRcan':       1 'canvas' widget 
##
##  3) Define bindings:
##       - button1-release on either of the 2 checkbuttons (the 'equilateral'
##         and 'corner-trace' requests) should cause a redraw
##       - change of the N-sides indicator (polygon type) --- i.e.
##         button1-release on that 'minilistbox' --- should cause a redraw
##       - change of the rotation indicator (polygon orientation) --- i.e.
##         button1-release on that 'minilistbox' --- should cause a redraw
##       - change of inside or outside color should cause a redraw --- i.e.
##         the commands for the 2 color-setting buttons should end with a redraw
##
##  4) Define procs:
##
##     - 'ReDraw'        - draws the polygon/star on the canvas
##     - 'RoundPoly'     - handles making the round corners
##     - '_RoundPoly2'   - helps to make the round corners
##     - 'rp'            - makes the vertices of an N-sided 'regular' polygon
##     - 'MakeStar'      - makes the vertices of a 5-pointed star
## 
##     - 'set_color_inside'  shows a color selector GUI and uses the
##                           user-selected color to redraw (re-fill) the
##                           polygons on the canvas in the specified color
##
##     - 'set_color_outside' shows a color selector GUI and uses the
##                           user-selected color to reset the color of
##                           the canvas background
##
##  5) Additional GUI initialization:  Execute proc 'ReDraw' once with
##                                     an initial, example set of parms
##                                     --- Nsides curRADIUS CINhex COUThex
##                                     (poly-type, radius and 2 colors) ---
##                                     to start with a rounded polygon on
##                                     the canvas rather than a blank canvas.
## 
##+########################################################################
## DEVELOPED WITH:
##   Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october release, 'Karmic Koala').
##
##   $ wish
##   % puts "$tcl_version $tk_version"
##                                  showed   8.5 8.5   on Ubuntu 9.10
##    after Tcl-Tk 8.4 was replaced by 8.5 --- to get anti-aliased fonts.
##+#######################################################################
## MAINTENANCE HISTORY:
## Created by: Blaise Montandon 2012aug15
## Changed by: ...... ......... 2012
##+#######################################################################

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

wm title    . "Polygons with Round Corners on a Canvas"
wm iconname . "RoundPolys"

wm geometry . +15+30

## We allow the window to be resizable and we pack the canvas with
## '-fill both -expand 1' so that the canvas can be enlarged by enlarging
## the window.

## 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 ---
## and set the initial color for the polygon interior
## and the canvas background (outside the polygon).
##+######################################################

tk_setPalette "#e0e0e0"

## Initialize the 'inside' and 'outside' colors for the canvas.

# set CINr 255
# set CINg 255
# set CINb 255
set CINr 100
set CINg 100
set CINb 240
set CINhex [format "#%02X%02X%02X" $CINr $CINg $CINb]

# set COUTr 0
# set COUTg 0
# set COUTb 0
set COUTr 60
set COUTg 60
set COUTb 60
set COUThex [format "#%02X%02X%02X" $COUTr $COUTg $COUTb]


##+########################################################
## Use a VARIABLE-WIDTH FONT for label and button widgets.
##
## Use a FIXED-WIDTH FONT for listboxes (and
## entry fields, if any).
##+########################################################

font create fontTEMP_varwidth \
   -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_fixedwidth  \
   -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, and padding for Buttons)
##+###########################################################

set initCanWidthPx 400
set initCanHeightPx 300
set minCanHeightPx 24

# set BDwidthPx_canvas 2
  set BDwidthPx_canvas 0


## BUTTON (and LABEL) geom parameters:

set PADXpx_button 0
set PADYpx_button 0
set BDwidthPx_button 2


## SCALE geom parameters:

set BDwidthPx_scale 2

set initScaleLengthPx 200


##+###################################################################
## Set a MINSIZE of the window.
##
## For width, allow for the minwidth of the '.fRbuttons' frame:
##            about 3 buttons (Exit,Color1,Color2), and
##            2 checkboxes, and a label with current polygon-type
##            and current rotation angle shown.
##
## For height, allow for a canvas at least 24 pixels high, and
##             2 chars high for the scale widget height in the
##            .fRpolyspecs' frame, and
##             2 chars high for the widgets in the '.fRbuttons' frame.
##+###################################################################

set minWinWidthPx [font measure fontTEMP_fixedwidth \
      "ExitColor1Color2EquilateralRotation  Current  PolyID  Angle"]

## Add some pixels to account for right-left-side window decoration
## (about 8 pixels), about 6x8 pixels for borders/padding for frames
## and 3 buttons and 2 checkboxes and 1 label (6 widgets).

set minWinWidthPx [expr 56 + $minWinWidthPx]


## FOR MIN HEIGHT:

set CharHeightPx [font metrics fontTEMP_fixedwidth -linespace]

set minWinHeightPx [expr $minCanHeightPx + 4 * $CharHeightPx]

## Add about 28 pixels for top-bottom window decoration,
## about 3x8 pixels for each of the 3 stacked frames and their
## widgets (their borders/padding).

set minWinHeightPx [expr $minWinHeightPx + 52]


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

wm minsize . $minWinWidthPx $minWinHeightPx


##+################################################################
## DEFINE *ALL* THE FRAMES:
##
##   Top-level : 'fRbuttons'  '.fRpolyspecs'  'fRcan'
##
##   Sub-frames: none
##+################################################################

# set BDwidth_frame 0
  set BDwidth_frame 2

# set RELIEF_frame raised
  set RELIEF_frame flat


frame .fRbuttons   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

frame .fRpolyspecs -relief raised         -borderwidth 2

frame .fRcan       -relief $RELIEF_frame  -borderwidth $BDwidth_frame


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

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

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


##+#########################
## DEFINE PROC 'minilistbox'
## (for use in making a couple of widgets below)
##+##############################################################
## By using the global variables
##          - fontTEMP_SMALL_fixedwidth 
##          - fontTEMP_SMALL_varwidth
##          - listboxBKGD
##          - listboxWIDTHchars
## for the decorative & geometric elements/parameters of the GUI,
## we keep the arguments of this widget-made-on-the-fly down
## to the 5 MAIN ELEMENTS/VARIABLES --- 3 INPUT AND 1 OUTPUT AND 1 CMD:
##
## - the parent widget/window,
##
## - an option/line at which to initially position the list in
##   the listbox (with the 'see' command),
##
## - an options list,
##
## - the name of the variable that is to hold the user-selected option,
##   i.e. a list-line (the result/output)
##   --- retrieved from the listbox with 'curselection' and 'get',
##
## - a command (proc --- and parameters, if any) to be executed at a
##   button1-release on this widget's frame.        
##+##############################################################

proc minilistbox {w opt1 optslist seloptvar mlbProc} {

   global fontTEMP_SMALL_fixedwidth fontTEMP_SMALL_varwidth \
          listboxBKGD listboxWIDTHchars

   ##+#####################################
   ## DEFINE-and-PACK the widget SUB-FRAMES:
   ## '.frup-down' for 2 up and down buttons
   ## and '.fRopts' for the listbox.
   ## Pack them side by side.
   ##+#####################################

   frame $w.fRup-down -relief flat -bd 2
   frame $w.fRopts    -relief flat -bd 2

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


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

   ## We comment-out this label definition (and its pack statement)
   ## to reduce the height of this 'minilistbox' widget.
   ## See the label definition statement for frame .fRopts, below.
   # label $w.fRup-down.label \
   #    -text " " \
   #    -anchor w \
   #    -relief flat

   button $w.fRup-down.buttUP \
      -text "Up" \
      -font fontTEMP_SMALL_varwidth \
      -width 3 -height 1 \
      -pady 1 \
      -padx 0 \
      -command [list $w.fRopts.listbox yview scroll -1 unit]

   button $w.fRup-down.buttDOWN \
      -text "Dwn" \
      -width 3  -height 1 \
      -font fontTEMP_SMALL_varwidth \
      -pady 1 \
      -padx 0 \
      -command [list $w.fRopts.listbox yview scroll +1 unit]

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

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


   ##+####################################################
   ## In FRAME '.fRopts',
   ## DEFINE-and-PACK an info label and a listbox widget.
   ##+####################################################

   ## We comment-out this label definition (and its pack statement)
   ## to reduce the height of this 'minilistbox' widget.
   ## The user could supply a label, say to the left of this
   ## 'minilistbox' widget, using a label-def in their Tk script.
   # label $w.fRopts.label \
   #   -text "Up/dwn ; click a line:" \
   #   -font fontTEMP_SMALL_varwidth \
   #   -anchor w \
   #   -relief flat

   listbox $w.fRopts.listbox \
      -font fontTEMP_SMALL_fixedwidth \
      -height 3 \
      -width $listboxWIDTHchars \
      -bg "$listboxBKGD" \
      -state normal

   foreach optline $optslist {
      $w.fRopts.listbox insert end $optline
   }

   # pack $w.fRopts.label \
   #   -side top \
   #   -anchor n \
   #   -fill x \
   #   -expand 0

   pack $w.fRopts.listbox \
      -side top \
      -anchor n \
      -fill x \
      -expand 0


   ##+###################################################
   ## POSITION the list at the 'opt1' line, using 'see'.
   ##   And make the opt1 line the default selection. (?)
   ##+###################################################

   set INDEXofOPT1 [ lsearch -exact $optslist $opt1 ]
   
   if { "$INDEXofOPT1" != "-1" } {

      set seeINDEX [expr $INDEXofOPT1 - 1 ]
      if { "$seeINDEX" < "0" } { 
         set seeINDEX "0"
      }

      $w.fRopts.listbox see $seeINDEX

      ## Comment this to de-activate it?
      $w.fRopts.listbox selection set $INDEXofOPT1

   }
   ## END OF if { "$INDEXofOPT1" != "-1" }


   ##+########################################################
   ## PROC for the following button1-release BINDING:  getline
   ##+########################################################

   proc getline {w outvar passedproc} {

      ## This 'upvar' associates the local var 'selectline' with
      ## the outer var that is to contain the listbox selection.
      ## It is like an EQUIVALENCE statement in FORTRAN.
      upvar #0 $outvar selectline

      set sel_index [ $w.fRopts.listbox curselection ]

      ## FOR TESTING:
      #  puts "sel_index: $sel_index"

      if { $sel_index != "" } {
         set selectline [ $w.fRopts.listbox get $sel_index ]
      } else {
         set selectline ""
      }

      eval set $outvar "$selectline"

      ## FOR TESTING:
      #   puts "selectline: $selectline"
      ##   puts "Nsides: $Nsides"
      ##   puts "theta0deg: $theta0deg"
      #   puts "outvar: [expr \$$outvar]"


      eval $passedproc
   }
   ## END OF proc getline


   ##+#####################################################
   ## SET BINDING on the listbox in this new-widget so that
   ##         <ButtonRelease-1> puts a selected line of the
   ##        listbox in a specified var and executes a
   ##        specified command/proc.
   ##+#####################################################

   bind  $w.fRopts.listbox <ButtonRelease-1> "getline $w $seloptvar \"$mlbProc\""

}
## END OF 'minlistbox' PROC

## Let us define those 4 global vars for the  'minlistbox' proc.

font create fontTEMP_SMALL_fixedwidth  \
   -family {liberation mono} \
   -size -10 \
   -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


font create fontTEMP_SMALL_varwidth \
   -family {comic sans ms} \
   -size -10 \
   -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

set listboxBKGD "#f0f0f0"
set listboxWIDTHchars 15


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


##+#####################################################################
## In the '.fRbuttons' FRAME  ---  DEFINE-and-PACK
##    - an exit-button,
## and
##    - 2 buttons ( to specify 'inside' color and 'outside' color)
## and
##   - a CHECKBUTTON (to request an EQUILATERAL polygon)
## and
##   - a CHECKBUTTON (to request a 'trace' of the polygon corners/points)
##+#####################################################################

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

button .fRbuttons.buttCIN \
   -text "\
Inside
Color" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {set_color_inside}

button .fRbuttons.buttCOUT \
   -text "\
Outside
Color" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {set_color_outside}


set polygonEQUILAT 1

checkbutton .fRbuttons.chkbuttEQUILAT \
   -text "Equilateral" \
   -font  fontTEMP_varwidth \
   -variable polygonEQUILAT \
   -selectcolor "#cccccc" \
   -relief raised

set polygonTRACE 0

checkbutton .fRbuttons.chkbuttTRACE \
   -text "Show Vertices" \
   -font  fontTEMP_varwidth \
   -variable polygonTRACE \
   -selectcolor "#cccccc" \
   -relief raised

label .fRbuttons.labelPARMS \
   -text "" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd $BDwidthPx_button


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

pack .fRbuttons.buttEXIT \
     .fRbuttons.buttCIN \
     .fRbuttons.buttCOUT \
     .fRbuttons.chkbuttEQUILAT \
     .fRbuttons.chkbuttTRACE \
     .fRbuttons.labelPARMS \
      -side left \
      -anchor w \
      -fill none \
      -expand 0



##+##################################################################
## In the '.fRpolyspecs' FRAME ----  DEFINE-and-PACK 
##   - a LABEL widget
##   - a 'minilistbox' widget for polygon-type
##   - a LABEL widget
##   - a 'minlistbox' widget for rotation/angle (polygon orientation)
##   - a LABEL widget
##   - a SCALE widget, for radius of the rounded corners/points
##+###################################################################

label .fRpolyspecs.labelNsides \
   -text "\
Polygon type
(N sides ; 0 is
5-pointed-star)" \
   -font fontTEMP_SMALL_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd $BDwidthPx_button


## DEFINE the 'minilistbox' widget for polygon type (Nsides)

frame .fRpolyspecs.fRpolysides -relief flat -bd 0
set polynums { 0 3 4 5 6 7 8 9 10 }
set Nsides 5
minilistbox .fRpolyspecs.fRpolysides $Nsides $polynums Nsides "ReDraw 0"


label .fRpolyspecs.labelNinfo \
   -text "\
\ \ \ \ Rotation
     angle (CC):" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd $BDwidthPx_button


## DEFINE the 'minilistbox' widget for polygon rotation angle (orientation)

frame .fRpolyspecs.fRpolyangle -relief flat -bd 0
set polyangles { 0 45 90 135 180 225 270 315 }
set theta0deg 0
minilistbox .fRpolyspecs.fRpolyangle $theta0deg $polyangles theta0deg "ReDraw 0"


##+################################
## DEFINE the radius 'scale' widget
## including a 'label' widget.
##+################################

## Set the init value for the radius-scale  var.

set curRADIUSallVerts 25

## Set the MAX UNITS for the radius-scale,
## i.e. the upper limit of the range of values,

# set scaleMaxUnits 200
# set scaleMaxUnits [expr $initCanWidthPx / 4]
  set scaleMaxUnits [expr $initCanWidthPx / 2]


## Define a label widget to precede the radius-scale,
## followed by the scale:

label .fRpolyspecs.labelSCALE1 \
   -text "\
\ \ \ \ Radius for
     all corners:" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd $BDwidthPx_button

scale .fRpolyspecs.scale1 \
   -orient horizontal \
   -digits 0 \
   -from 0 -to $scaleMaxUnits \
   -length $initScaleLengthPx \
   -variable curRADIUSallVerts \
   -command "ReDraw"

## PACK the widgets of FRAME .fRpolyspecs ---
## label ; minilistbox-frame ; label ; minilistbox-frame ; label ; scale

pack .fRpolyspecs.labelNsides \
     .fRpolyspecs.fRpolysides \
     .fRpolyspecs.labelNinfo \
     .fRpolyspecs.fRpolyangle \
     .fRpolyspecs.labelSCALE1 \
      -side left \
      -anchor w \
      -fill none \
      -expand 0

pack .fRpolyspecs.scale1 \
      -side left \
      -anchor w \
      -fill x \
      -expand 1


##+######################################################
## DEFINE-and-PACK the 'canvas' widget
## in the '.fRcan' FRAME
##+######################################################

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

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


##+########################################
## END OF the DEFINITION OF THE GUI WIDGETS
##+########################################


##+###############################
## BINDINGS SECTION:
##+###############################

bind . <Configure> {ReDraw 0}

bind .fRbuttons.chkbuttEQUILAT <ButtonRelease-1> "ReDraw 0"

bind .fRbuttons.chkbuttTRACE <ButtonRelease-1> "ReDraw 0"



##+################################################################
## PROCS SECTION:
##  - ReDraw      - called in radius-scale '-command' and
##                  in several bindings
##  - RoundPoly   - called by ReDraw
##  - _RoundPoly2 - called by RoundPoly
##  - rp          - called by ReDraw
##  - MakeStar    - called by ReDraw
##
##  - set_color_inside  - called by  inside-color button '-command'
##  - set_color_outside - called by outside-color button '-command'
##+#################################################################


##+####################################################################
## proc ReDraw - called by '-command' on the radius-resetting scale widget
##               and when polygon-type is reset
##               and when regularity is reset
##               and when non-rounded polygon dotted-trace is requested.
##+####################################################################

## Set the drawing area proportion of the canvas once,
## when the GUI starts up.

set drawareaFactor 0.8

proc ReDraw {dummy} {

   global  curRADIUSallVerts Nsides theta0deg \
           drawareaFactor polygonEQUILAT polygonTRACE CINhex

   ## Get the current canvas width and height.
   set canWidth  [winfo width  .fRcan.can]
   set canHeight [winfo height .fRcan.can]

   ## Set the width and height of the polygon draw area (a rectangle).
   set W [expr $canWidth  * $drawareaFactor]
   set H [expr $canHeight * $drawareaFactor]

   ## If a regular polygon is wanted, make the drawing area square.
   if { $polygonEQUILAT == 1} {
      if {$W > $H} { set W $H } else { set H $W }
   }
      
   ## Set the upper left point of the drawing area.
   set x0 [expr ( $canWidth - $W ) / 2 ]
   set y0 [expr ( $canHeight - $H ) / 2 ]

   ## Set the lower right of the drawing area.
   set x1 [expr $x0 + $W]
   set y1 [expr $y0 + $H]
 
   ## Load var 'xy' with the vertices of the polygon.
   if {$Nsides == 0} {
        set xm [expr $canWidth / 2 ]
        set ym [expr $canHeight / 2 ]
        set rad  [expr $W / 4 ]
        set xy [MakeStar  $xm $ym $rad]
      # set xy [MakeStar  0 0 $W]
   } else {
      set xy [rp $x0 $y0 $x1 $y1 $Nsides]
   }

   ## Set the radius for each vertex.
   ## For now, all radii are set the same, but RoundPoly is
   ## written to allow a different radius for each vertex.
   set radii {}
   foreach {x y} $xy {
         lappend radii $curRADIUSallVerts
   }
 
   ## Clear all polygons off the canvas.
   .fRcan.can delete poly

   ## If the user wants the corners indicated, draw the non-rounded polygon
   ## first, in gray, before the rounded-polygon draw and the dotted-lines draw.
   if { $polygonTRACE == 1 } {
      .fRcan.can create poly $xy -fill gray90 -outline black -dash . -tags poly
   }

   ## Draw the rounded polygon (filled), in the requested inside-color.
   RoundPoly .fRcan.can $xy $radii -fill $CINhex -outline $CINhex -tags poly

   ## If the user wants the dotted-lines of the unrounded polygon border drawn,
   ## draw the dotted lines last, after the rounded-filled-polygon draw.
   if { $polygonTRACE == 1 } {
      .fRcan.can create poly $xy -fill {} -outline black -dash . -tags poly
   }

   .fRbuttons.labelPARMS configure -text \
         "    Current   PolyID=$Nsides   OrientAngle=$theta0deg (cc)"

}
## END OF proc ReDraw


##+###################################################################
##
## proc RoundPoly:   called by ReDraw
##
## Draws a polygon with rounded corners in the canvas, based
## on ideas and code from "Drawing rounded rectangles"
##
## Parameters:
##       w - Path name of the canvas
##       xy - list of coordinates of the vertices of the polygon
##       radii - list of radii --- the bend at each vertex
##       args - Other args suitable to a 'polygon' item on the canvas
##
## Results:
##     -  Returns the canvas item number of the rounded polygon.
##     -  Creates a rounded polygon in the canvas.
##
##+###################################################################
 
proc RoundPoly {w xy radii args} {

    set lenXY [llength $xy]
    set lenR [llength $radii]

    if {$lenXY != 2 * $lenR} {
        error "wrong number of vertices and radii"
    }
 
    # Walk down vertices keeping previous, current and next
    foreach {x0 y0} [lrange $xy end-1 end] break

    foreach {x1 y1} $xy break

    eval lappend xy [lrange $xy 0 1]

    set knots {}         ;# These are the control points
 
    for {set i 0} {$i < $lenXY} {incr i 2} {
        set radius [lindex $radii [expr {$i/2}]]
        set r [winfo pixels $w $radius]
 
        foreach {x2 y2} [lrange $xy [expr {$i + 2}] [expr {$i + 3}]] break
        set z [_RoundPoly2 $x0 $y0 $x1 $y1 $x2 $y2 $r]
        eval lappend knots $z
 
        foreach {x0 y0} [list $x1 $y1] break    ;# Current becomes previous
        foreach {x1 y1} [list $x2 $y2] break    ;# Next becomes current
    }
    set n [eval $w create polygon $knots -smooth 1 $args]
    return $n
}
## END OF proc RoundPoly


##+##########################################
## proc _RoundPoly2 - called by RoundPoly
##+##########################################

proc _RoundPoly2 {x0 y0 x1 y1 x2 y2 radius} {
    set d [expr { 2 * $radius }]
    set maxr 0.75
 
    set v1x [expr {$x0 - $x1}]
    set v1y [expr {$y0 - $y1}]
    set v2x [expr {$x2 - $x1}]
    set v2y [expr {$y2 - $y1}]
 
    set vlen1 [expr {sqrt($v1x*$v1x + $v1y*$v1y)}]
    set vlen2 [expr {sqrt($v2x*$v2x + $v2y*$v2y)}]
    if {$d > $maxr * $vlen1} {
        set d [expr {$maxr * $vlen1}]
    }
    if {$d > $maxr * $vlen2} {
        set d [expr {$maxr * $vlen2}]
    }
 
    lappend xy [expr {$x1 + $d * $v1x/$vlen1}] [expr {$y1 + $d * $v1y/$vlen1}]
    lappend xy $x1 $y1
    lappend xy [expr {$x1 + $d * $v2x/$vlen2}] [expr {$y1 + $d * $v2y/$vlen2}]
 
    return $xy
}
## END OF proc _RoundPoly2


##+#####################################################################
## proc rp - called by ReDraw - draws a regular polygon of n sides.
##
## Code from 'Regular polygons' at
## http://wiki.tcl.tk/8398 R. Suchenwirth
##+#####################################################################

set pi [expr 4 * atan(1)]

proc rp {x0 y0 x1 y1 {n 0}} {

   global theta0deg pi

   set theta0rad [expr $pi * ($theta0deg / 180.0) ]

   ## FOR TESTING:
   #   puts "rp: theta0rad = $theta0rad"


   ## Apparently RS or KPV set the initial angle, for the location
   ## of the first vertex, at 6 * 45 degrees = 270 degrees.
   # set th [expr {atan(1)*6}] ;#top

   ## set th [expr $theta0rad + (3*$pi/2)]
      set th [expr (3*$pi/2) - $theta0rad]

   ## FOR TESTING:
   #   puts "rp: init th = $th"

    set xm [expr {($x0+$x1)/2.}]
    set ym [expr {($y0+$y1)/2.}]
    set rx [expr {$xm-$x0}]
    set ry [expr {$ym-$y0}]
    if {$n==0} {
        set n [expr {round(($rx+$ry)*0.5)}]
    }
    set step [expr {atan(1)*8/$n}]
    set res ""


    for {set i 0} {$i<$n} {incr i} {
        lappend res \
            [expr {$xm+$rx*cos($th)}] \
            [expr {$ym+$ry*sin($th)}]
        set th [expr {$th+$step}]

         ## FOR TESTING:
         # puts "rp: th = $th"

    }
    set res
}
## END OF proc rp


##+#############################################################
## proc MakeStar - called by ReDraw
##
## Code from 'Sun, moon, and stars' at
## http://wiki.tcl.tk/1247 R. Suchenwirth
##+#############################################################

proc MakeStar {x y delta} {
    set pi [expr {atan(1) * 4}]
 
    # Compute distance to inner corner
    #set x1 [expr {cos(54 * $pi/180)}]          ;# Unit vector to inner point
    set y1 [expr {sin(54 * $pi/180)}]
    set y2 [expr {$delta * sin(18 * $pi/180)}]  ;# Y value to match
    set delta2 [expr {$y2 / $y1}]
 
    # Now get all coordinates of the 5 outer and 5 inner points
    for {set i 0} {$i < 10} {incr i} {
        set d [expr {($i % 2) == 0 ? $delta : $delta2}]
        set theta [expr {(90 + 36 * $i) * $pi / 180}]
        set x1 [expr {$x + $d * cos($theta)}]
        set y1 [expr {$y - $d * sin($theta)}]
 
        lappend coords $x1 $y1
    }
    return $coords
}
## END OF proc MakeStar


##+#####################################################################
## proc 'set_color_inside'
##+##################################################################### 
## PURPOSE:
##
##   This procedure is invoked to get an RGB triplet
##   via 3 RGB slider bars on the FE Color Selector GUI.
##
##   Uses that RGB value to set the color of all the tagged items
##   (ovals and rectangles) on the canvas.
##
## Arguments: none
##
## CALLED BY:  .fRbuttons.buttCIN  button
##+#####################################################################

proc set_color_inside {} {

   global CINr CINg CINb CINhex curRADIUSallVerts curRADIUSevenVerts
   # global feDIR_tkguis

   ## FOR TESTING:
   #    puts "CINr: $CINr"
   #    puts "CINg: $CINg"
   #    puts "CINb: $CINb"

   set TEMPrgb [ exec \
       ./sho_colorvals_via_sliders3rgb.tk \
       $CINr  $CINg $CINb]

   #   $feDIR_tkguis/sho_colorvals_via_sliders3rgb.tk \

   ## FOR TESTING:
   #    puts "TEMPrgb: $TEMPrgb"

   if { "$TEMPrgb" == "" } { return }
 
   scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB

   set CINhex "#$hexRGB"
   set CINr $r255
   set CINg $g255
   set CINb $b255

   ## Call proc ReDraw to redraw the geometry in the new interior color.

   ReDraw 0

}
## END OF proc 'set_color_inside'


##+#####################################################################
## proc 'set_color_outside'
##+##################################################################### 
## PURPOSE:
##
##   This procedure is invoked to get an RGB triplet
##   via 3 RGB slider bars on the FE Color Selector GUI.
##
##   Uses that RGB value to set the color of the canvas ---
##   on which all the tagged items (ovals and rectangles) lie.
##
## Arguments: none
##
## CALLED BY:  .fRbuttons.buttCOUT  button
##+#####################################################################

proc set_color_outside {} {

   global COUTr COUTg COUTb COUThex
   # global feDIR_tkguis

   ## FOR TESTING:
   #    puts "COUTr: $COUTr"
   #    puts "COUTg: $COUTb"
   #    puts "COUTb: $COUTb"

   set TEMPrgb [ exec \
       ./sho_colorvals_via_sliders3rgb.tk \
       $COUTr  $COUTg $COUTb]

   #   $feDIR_tkguis/sho_colorvals_via_sliders3rgb.tk \

   ## FOR TESTING:
   #    puts "TEMPrgb: $TEMPrgb"

   if { "$TEMPrgb" == "" } { return }
 
   scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB

   set COUThex "#$hexRGB"
   set COUTr $r255
   set COUTg $g255
   set COUTb $b255

   ## Set the color of the canvas.

   .fRcan.can config -bg $COUThex

}
## END OF proc 'set_color_outside'


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

ReDraw 0

.fRcan.can config -bg $COUThex


Here is an example of using the GUI with the EQUILATERAL checkbutton un-checked --- and with N = 5 --- and with the canvas 'squashed'.

As I mentioned above, if I get tired of using rounded rectangles (with transparent corners and transparent surrounding pixels) for wide buttons on GUI's (for example, 'drawers' in 'toolchest' GUI's), I could try 'flattened', rounded pentagons. ..... No?

Then maybe 'flattened' hexagons or octagons. Give it a try.

(I might have to post-process the captured image with a blur utility to feather those jagged, non-horizontal-and-non-vertical edges. It then becomes important what color to use for the background.)

______________________________________________________________

I want to thank Keith Vetter and Richard Suchenwirth for their mathematical-geometrical polygon-making procs which allowed me to concentrate on making the various features of this GUI.

I used Vetter's 'RoundPoly' and '_RoundPoly2' procs without a single change.

I believe the 'MakeStar' proc is Vetter's enhancement of an original star-making proc by Suchenwirth. I have not changed that proc, and as a result, choosing different 'Rotation angles' in that 'minilistbox' of the GUI does not have any effect on the star. I leave it as an exercise for the reader to put some angle-code in the 'MakeStar' proc.

(Actually, the rotation feature is just a convenience for rapidly rotating the image. One could always capture the image in a file and read the file into an image editor like 'mtpaint', to rotate the image.)

I made a couple of minor changes to Suchenwirth's 'rp' (regular polygon) proc to allow for the 'Rotation angle' option to work.

The proc that I made the most changes to was Vetter's 'doit' proc --- which I renamed to 'ReDraw'.

Thanks so much, RS and KPV. You guys are awesome.

____________________________________________________________

Now I have a way to make rounded-polygon 'masks' to use with the 'IMAGEtools' utilities in my 'feNautilusScripts' system --- to make rounded polygons to use as background images in the 'drawers' and 'toolchests' of my 'feAppMenus' and 'feHandyTools' systems.

I can now make equilateral rounded-polygons, such as pentagons and heptagons, and reduce their size, to make unusual-shaped 'bullets' for items on the toolchests.

As I mentioned at the bottom of the Experiments in making embellished GUI's page, perhaps we can start a gallery page showing some particularly well-embellished toolchests/GUI's ...

Or perhaps a gallery page of particularly nice buttons and backgrounds and icons that can be used by Tcler's for backgrounds/embellishments in their GUI's.

_____________________________________________________________

Here is an image of the 5-pointed star. Nice work, Vetter --- in handling both convex and concave corners.

By the way, the script always draws the star in an equilateral fashion --- even with the 'Equilateral' checkbutton un-checked. It is left as an exercise for the reader to allow the star's aspect ratio to mimic the aspect ratio of the canvas, when the 'Equilateral' checkbutton un-checked.