tkTemperature-Convert-Select - Fahrenheit, Centigrade, Kelvin

uniquename - 2014jan17

In the past year (2013), I posted some 'converter-selector' Tk GUI utilities on this wiki:

1) an angle converter-selector utility --- at 'tkAngle-Convert-Select - radians, degrees, percents-of-circumference-of-circle' [L1 ]

2) a velocity converter-selector utility --- at 'tkSpeedGun-Convert-Select - kilometers/hr, miles/hr, lightyears/billion-years' [L2 ]

I based those GUI's on my color-selector GUI (with 3 'slider buttons') --- whose code I contributed at A non-obfuscated color selector GUI.

However, on the color-selector GUI, the 3 slider-buttons were moved independently of each other.

For the 'angle converter' GUI and for the 'speed converter' GUI, the 3 sliders would need to move together. The Tk 'scale' widget handles that nicely. In Tk, a variable is associated with each 'slider', and any time the value of that variable changes, the slider moves to reflect the changed value.

I just needed to make sure that when any of the 3 variables changed, the other 2 variables were changed correspondingly.

---

I have found (occasionally) that I have need for OTHER types of converters:

  • for temperature (Fahrenheit, Centigrade, Kelvin)
  • for volume (tablespoons, cups, quarts, liters, gallons, barrels, etc.),
  • for distance (inches, centimeters, meters, yards, miles, kilometers, light-years, etc.)

On this page, I present the code for a temperature converter-selector utility.

---

VERTICAL SCALES:

On the several converter-selector utilities that I have written so far, the 3 scale widgets on the GUI's were oriented horizontally.

However, for the temperature converter GUI, it seemed appropriate to orient the 3 scale widgets vertically --- like 3 thermometers.

---

MELTING/BOILING INFO:

The converter would seem rather plain (and rather uninformative) if just the 3 scales were provided --- with a display of a temperature in the 3 different units (Fahrenheit, Centigrade, and Kelvin).

So to provide some perspective, I decided to show on the GUI a line of text, corresponding to the current temperature setting. The text indicates boiling or melting point information for a material --- at a temperature in the 'neighborhood' of the current temperature setting of the scale widgets.

And to get an overview of the range of melting and boiling points of various materials, throughout the temperature range of the scale widgets, the 'Help' button on the GUI provides text which includes a table of temperatures that are melting/boiling points of various materials (fundamental elements, molecules, mixtures of molecules --- such as copper, carbon-dioxide, brass, various steels, and glazes).

The user can click on the 'Help' button to see that overview of 'phenomena' that occur over a wide temperature range.

---

TEMPERATURE COLORS:

To further liven up the GUI, I have provided some dynamic color change:

The color of the 'troughs' of the scale widgets change as the temperature is changed.

(I initially changed the entire color palette of the GUI as the temperature was changed, but that seemed like too much color change. It is easy to comment and un-comment a few statements in the code below to see what that more extensive color-change looks like.)

There are various ways one could implement the color change:

1) A two-phase change --- say from blue to red as temperature goes from absolute zero to about twice the boiling point of water. Then from red (hot) to white (hot) from that point to the top of the temperature scales.

2) A 3-phase change --- say from blue to red as temperature goes from absolute zero to about twice the boiling point of water. Then from red to yellow from that point to about 2000 degrees. Then from yellow to white from about 2000 degrees to the top of the temperature scales.

---

SOME IMAGES:

So I set to work making a GUI with these features, and I ended up with the GUI seen in the following image.

tkTemperatureConvertSelect_initial_screenshot_799x487.jpg

Whenever ONE of the 'sliders' is moved with mouse-button1 :

  • the other TWO sliders move (showing the converted values)
  • 3 text areas (on a line above the scale widgets) change to present the current 3 values for the temperature (available for copy-paste into another window on the desktop)

The new values show (and the colors change) as soon as mouse-button1 is released.

Note that when the temperature is around 0 degrees Centigrade, a line of text indicates that 0 Centigrade is the melting point of ice.

---

Here is an image in which the temperature has been increased to 1537 degrees Centigrade --- and the line of text indicates that iron melts in the neighborhood of this temperature.

tkTemperatureConvertSelect_iron_screenshot_799x485.jpg

In addition, I am using the 3-phase color-change described above, and the troughs of the scales have changed to a yellowish color.

---

The following image indicates that I have allowed the window to be resizable. In this case, I have dragged the lower-right corner of the window to make the GUI smaller.

tkTemperatureConvertSelect_tungsten_shrunkWindow_screenshot_556x416.jpg

Furthermore, the line of text indicates that tungsten melts in the neighborhood of this temperature --- and the troughs of the scales have changed to a whitish color.


The 'Help' button on the GUI provides pretty complete and detailed help for using the GUI. In particular, the help points out the following info.

*******************

FINE CONTROL OF THE SLIDERS:

Most people know that you can drag the sliders by clicking on the 'slider button' with mouse-button1 and dragging the slider button. This is a rather coarse way of moving the slider button.

It is not so obvious that the Tk scale widget also allows you to move the 'slider button' by clicking in the 'trough' on EITHER SIDE of the slider button. By repeated clicking in the trough, the slider can be advanced one scale-resolution unit per click.

Furthermore, one can RAPIDLY move the slider button ONE RESOLUTION UNIT per 'auto-click' by clicking in the trough and HOLDING down the mouse button. The slider moves one resolution unit repeatedly UNTIL the mouse button is RELEASED.


The code

Below, I provide the Tk script code for this 'tkTemperature-Convert-Select' utility.

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,
     text-array-for-labels-etc).

  1a) Define ALL frames (and sub-frames, if any).
  1b) Pack   ALL frames and sub-frames.

  2) Define & pack all widgets in the frames, frame by frame.
              Within each frame, define ALL the widgets.
              Then pack the widgets.

  3) Define keyboard and mouse/touchpad/touch-sensitive-screen action
     BINDINGS, if needed.

  4) Define PROCS, if needed.

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

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

This structure makes it easy for me to find code sections --- while generating and testing a Tk script, and when looking for code snippets to include in other scripts (code re-use).

I call your attention to step-zero. One new thing that I have started doing recently is using a text-array for text in labels, buttons, and other widgets in the GUI. This can make it easier for people to internationalize my scripts. I will be using a text-array like this in most of my scripts in the future.


Experimenting with the GUI

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

That helps me when I am initially testing the behavior of a GUI (the various widgets within it) as I resize the main window.

In this particular GUI, I allowed the user to resize the window.

However, one could un-comment the statement

   wm resizable . 0 0

and then the user would not be able to change the size of the window from its initially built size.

With the window resizable, you can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various frames and widgets --- to get the widget behavior that you want as the window is resized.

___

Additional experimentation: You might want to change the fonts used for the various GUI widgets. For example, you could change '-weight' from 'bold' to 'normal' --- or '-slant' from 'roman' to 'italic'. Or change font families.

In fact, you may NEED to change the font families, because the families I used may not be available on your computer --- and the default font that the 'wish' interpreter chooses may not be very pleasing.

I use variables to set geometry parameters of widgets --- parameters such as border-widths and padding. And I have included the '-relief' parameter on the definitions of frames and widgets. Feel free to experiment with those 'appearance' parameters as well.


Some features in the code

That said, here's the code --- with plenty of comments to describe what most of the code-sections are doing.

You can look at the top of the PROCS section of the code to see a list of the procs used in this script, along with brief descriptions of how they are called and what they do.

The main procs are simply

     'temperature_update'   - called by button1-release bindings on 3 scale widgets.
                              (One could someday try calling this proc in the
                               '-command' options on the 3 scales.)

     'set_temperatureColor' - called by the 'temperature_update' proc

     'get_centitext'        - called by the 'temperature_update' proc

     'put_vars'             - called by the 'UseIt' button

     'popup_msg_var_scroll' - called by the 'Help' button

The main proc is the 'temperature_update' proc. As any of the 3 sliders is changed, it

  • sets the value for the other 2 sliders
  • updates the small text widgets that hold the temperature in 3 different units
  • calls 'get_centitext' to set the melting/boiling info-line on the GUI
  • calls 'set_temperatureColor' to set the color of some part(s) of the GUI.

It is my hope that the copious comments in the code will help Tcl-Tk coding 'newbies' get started in making GUI's like this.

Without the comments, potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch videos of volcanos, wildfires, melting glaciers, people in burning clothes, and other elevated-temperature phenomena.


 Code for Tk script 'tkTemperature-Convert-Select_F-C-K.tk' :
#!/usr/bin/wish -f
##
##+#######################################################################
## NOTE:
##   If the 'wish' interpreter is in another directory, like
##   /usr/local/bin, you, as root, can make a soft-link from 'wish' there
##   to /usr/bin/wish --- with a command like
##             ln -s /usr/local/bin/wish  /usr/bin/wish
##   The form of this command:
##             ln -s <filename-of-existing-file> <name-of-new-link-file>
##+#######################################################################
## Tk SCRIPT NAME:   tkTemperature-Convert-Select_F-C-K.tk           
##
##+#######################################################################
## PURPOSE:  This TkGUI script provides a GUI for showing temperatures
##           between absolute zero and several thousand degrees ---
##           in three different units:
##             - Fahrenheit
##             - Centigrade
##             - Kelvin.
##
##           These 3 different units are shown (and selected for conversion)
##           via 3 'sliders' of 3 Tk 'scale' widgets. 
##
##           These 3 different (corresponding) numbers are also shown
##           in 3 'text' widgets, a few characters wide --- from which
##           a user can copy-and-paste the numbers from this GUI window 
##           to some other window. 
##
##           For any (completed) 'slider' change, this GUI script updates
##           the location of the other 2 'sliders'.
##
##           The 3 text widgets, showing the temperature in the 3 different
##           units, are also updated whenever any one of the 3 scale widget
##           'sliders' are changed.
##
##           By clicking on a 'UseIt' button on the GUI, this script
##           returns a string containing the current temperature (in the
##           3 different units) to a calling script/application.
##
##           This script will accept a temperature (in Centigrade) as an
##           argument, which is used to initialize the
##             - setting of the 3 'sliders', and
##             - the numbers shown in the 3 small text widgets.
##
## SOME USES:
##
##       1)  Useful for helping math-science students get a visual
##           'feel' for the relation between Fahrenheit, Centigrade,
##           and Kelvin.
##
##       2)  Also could be useful for programmers (example: Tcl-Tk programmers)
##           for determining temperatures (example: Centigrade from Fahrenheit)
##           to be used in various Tcl-Tk apps.
##
##       3)  IN A SHELL SCRIPT OR ANOTHER TK SCRIPT, this Tk script can
##           ACT AS AN TEMPERATURE SELECTOR by passing the current temperature
##           (in the 3 different units) to stdout, when the OK/UseIt button
##           is clicked.
##           Example output string:  32.0  0.0  273.0
##                                   (Fahrenheit,Centigrade,Kelvin)
##+########################################################################
## 'CANONICAL' STRUCTURE OF THIS CODE:
##
##  0) Set general window parms (win-name, win-position, win-color-scheme,
##     fonts, widget-geom-parms, win-size-control, text-array-for-labels-etc).
##  1a) Define ALL frames (and sub-frames).
##  1b) Pack   ALL the frames and sub-frames.
##  2) Define & pack all widgets in the frames, frame by frame.
##
##  3) Define key and mouse/touchpad/touch-sensitive-screen 'event'
##     BINDINGS, if needed.
##  4) Define PROCS, if needed.
##  5) Additional GUI INITIALIZATION (typically with one or two
##                procs in section 4), if needed.
##
##+#################################
## The code structure in more detail, for this particular script:
##
##  1a) Define ALL frames: 
##
##      Top-level :
##
##      'fRbuttons'    - to contain several buttons --- UseIt,Cancel,Help
##
##      'fRtexttemps'  - to contain 3 pairs of label-and-text widgets to
##                       display the current temperature, in the 3 different
##                       units, in the 3 text widgets.
##
##      'fRinfo'       - to contain a label widget, with changeable
##                       temperature and material-melting/boiling info
##
##      'fRsliders'    - to contain 3 sliders - 'scale' widgets, along with
##                       3 'label' widgets.
##
##      Sub-frames: 'fRsliders.fRfahr' , 'fRsliders.fRcent' , 'fRsliders.fRkelv'
##
##        'fRbuttons'   is to be on the top of the GUI,
##        'fRtexttemps' is to be just below that frame, and
##        'fRinfo'      is to be just below that frame, and
##        'fRsliders'   is to be on the bottom of the GUI.
##
##        The 'fRfahr', 'fRcent', 'fRkelv' sub-frames of the 'fRsliders' frame
##        are to be packed left to right.
##
##        The 3 scale widgets are to be oriented vertically in the
##        3 sub-frames.
##
##  1b) Pack ALL frames.
##
##  2) Define & pack all widgets in the frames -- basically going through
##     frames & their interiors in top-to-bottom and/or left-to-right order:
##
##  3) Define BINDINGS:  button-1 release bindings on the 3 scale widgets
##                       cause a call to the 'temperature_update' proc.
##                       For details, see the BINDINGS code section below.
##
##  4) Define PROCS:
##     'temperature_update'   - called by button1-release bindings on 3 scale widgets.
##                              (One could someday try calling this proc in the
##                               '-command' options on the 3 scales.)
##     'set_temperatureColor' - called by the 'temperature_update' proc
##     'get_centitext'        - called by the 'temperature_update' proc
##     'put_vars'             - called by the 'UseIt' button
##     'popup_msg_var_scroll' - called by the 'Help' button
##
##  5) Additional GUI INITIALIZATION:  set the HELPtext var for the Help button
##                                     and set a list-and-array for holding
##                                     melting-boiling/freezing-condensing info.
##
##+#######################################################################
## 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 2013aug22 Started on Ubuntu 9.10, based
##                                        on the color-selector script
##                                        of the FE (Freedom Environment)
##                                        subsystems. Ref: www.freedomenv.com   
## Changed by: Blaise Montandon 2013aug23 Added frame 'fRinfo'. Changed the
##                                        formula for the palette color change.
## Changed by: Blaise Montandon 2013aug24 Added vars LISTcentipairs,aRcentitext.
## Changed by: Blaise Montandon 2014jan16 Added a separate 'fRtextemps' frame.
##                                        Implemented the 'get_centitext' proc.
##                                        Added a color-setting method to the
##                                        'set_temperatureColor' proc.
##+############################################################################

##+#################################
## SET THE TOP WINDOW NAME.
##+#################################

wm title . \
   "tkTemperature-Convert-Select -  Fahrenheit, Centigrade, Kelvin"

wm iconname . "Temperature"

# catch { wm title    . "$env(FE_WIN_TITLE)" }
# catch { wm iconname . "$env(FE_ICON_TITLE)" }


##+###################################
##  SET THE TOP WINDOW POSITION.
##+###################################

wm geometry . +15+30

# catch {eval wm geometry . "$env(FE_TEMPERATURESEL_GEOM)" }


##+#######################################################################
## SET COLOR SCHEME (palette) FOR THE WINDOW.
##+#######################################################################

if {1} {
##  Gray palette 
set Rpal255 210
set Gpal255 210
set Bpal255 210
}

if {0} {
##  Bluish palette 
set Rpal255 100
set Gpal255 0
set Bpal255 255
}

if {0} {
##  Redish palette 
set Rpal255 255
set Gpal255 0
set Bpal255 100
}

## NOTE: Although we set the window palette here, we could reset
##       the color 'dynamically', according to current Kelvin value,
##       in the proc 'temperature_update'.
     
set hexCOLORpal [format "#%02X%02X%02X" $Rpal255 $Gpal255 $Bpal255]

tk_setPalette $hexCOLORpal


## Set color background for some widgets.

# set radbuttBKGD "#c0c0c0"
# set chkbuttBKGD "#c0c0c0"
# set listboxBKGD "#f0f0f0"
# set entryBKGD   "#f0f0f0"
set textBKGD    "#f0f0f0"


##+#######################################################################
## SET FONT ATTRIBUTE VARS to use in the 'font create' statements below.
##+#######################################################################

set FONTsize 14
set FONT_SMALLsize 12

## For variable width:

set FONT_varwidth \
   " -family {comic sans ms} -size -$FONTsize -weight bold -slant roman "

set FONT_SMALL_varwidth \
   " -family {comic sans ms} -size -$FONT_SMALLsize -weight normal -slant roman "


## For fixed width:

set FONT_fixedwidth \
   " -family {dejavu sans mono} -size -$FONTsize -weight bold -slant roman "


set FONT_SMALL_fixedwidth \
   " -family {dejavu sans mono} -size -$FONT_SMALLsize -weight normal -slant roman "


##+#####################################################################
## DEFINE (temporary) 'font create' NAMES to be used
## in '-font' widget specs below.
##+#####################################################################

eval font create fontTEMP_button  $FONT_varwidth
eval font create fontTEMP_label   $FONT_varwidth
eval font create fontTEMP_scale   $FONT_varwidth

# eval font create fontTEMP_entry   $FONT_fixedwidth
# eval font create fontTEMP_listbox $FONT_fixedwidth
# eval font create fontTEMP_msg     $FONT_fixedwidth

eval font create fontTEMP_text    $FONT_fixedwidth


# eval font create fontTEMP_SMALL_button  $FONT_SMALL_varwidth
# eval font create fontTEMP_SMALL_label   $FONT_SMALL_varwidth
# eval font create fontTEMP_SMALL_scale   $FONT_SMALL_varwidth

# eval font create fontTEMP_SMALL_entry   $FONT_SMALL_fixedwidth
# eval font create fontTEMP_SMALL_listbox $FONT_SMALL_fixedwidth
# eval font create fontTEMP_SMALL_msg     $FONT_SMALL_fixedwidth

# eval font create fontTEMP_SMALL_text    $FONT_SMALL_fixedwidth


##+#######################################################################
## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS.
## (e.g. padx,pady for buttons)
##+#######################################################################

## For BUTTON widgets:
   
set PADYpx_button 0
set PADXpx_button 0
set BDwidthPx_button 2


## For LABEL widgets:

set PADXpx_label 0
set PADYpx_label 0
set BDwidthPx_label 2


## SCALE geom parameters:

set BDwidthPx_scale 2


## For TEXT widgets:

set BDwidthPx_text 2


if {0} {
## CANVAS geom parms:
set initCanWidthPx  200
set initCanHeightPx 200
set minCanHeightPx 24
# set BDwidthPx_canvas 2
  set BDwidthPx_canvas 0
}


## For ENTRY widgets:

# set BDwidthPx_entry 2


## For LISTBOX widgets:

# set BDwidthPx_listbox 2



##+###################################################################
## Set a MINSIZE of the window (roughly).
##
## For WIDTH, allow for a minwidth of the '.fRbuttons' frame:
##            about 3 buttons (UseIt,Cancel,Help). OR allow for a
##            minwidth of the '.fRtexttemps' frame: 3 pairs
##            of label-and-text widgets.
##
## For HEIGHT, allow about
##             1 char   high for the '.fRbuttons' frame
##             1 char   high for the '.fRtexttemps' frame
##             1 char   high for the '.fRinfo'    frame
##           300 pixels high for the '.fRsliders' frame.
##+#######################################################################
## NOTE:
## We allow the window to be resizable and we pack the 'fRsliders' frame
## with '-fill both -expand 1' so that the sliders frame can be enlarged by
## enlarging the window.
##+#######################################################################

# set minWinWidthPx [font measure fontTEMP_button \
#    " UseIt  Cancel  Help"

set minWinWidthPx [font measure fontTEMP_button \
   "Fahrenheit: 32.0  Centigrade: 0.0  Kelvin: 273.0"]

## Add some pixels to account for right-left-side window decoration
## (about 8 pixels), about 6 widgets x 4 pixels/widget for borders/padding
## for 6 widgets --- label-and-text widget pairs.

set minWinWidthPx [expr {32 + $minWinWidthPx}]


## MIN HEIGHT ---
##             1 char high for the '.fRbuttons' frame
##             1 char high for the '.fRtexttemps' frame
##             1 char high for the '.fRinfo'    frame
##           300 pixels high for the '.fRsliders' frame

set CharHeightPx [font metrics fontTEMP_varwidth -linespace]

set minWinHeightPx [expr {300 + (3 * $CharHeightPx)}]

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

set minWinHeightPx [expr {40 + $minWinHeightPx}]


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

wm minsize . $minWinWidthPx $minWinHeightPx


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


##+########################################################################
## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI.
##     NOTE: This can aid INTERNATIONALIZATION. This array can
##           be set according to a nation/region parameter.
##+########################################################################

## if { "$VARlocale" == "en"}

## For '.fRbuttons' frame:

set aRtext(buttonUSEIT)  "UseIt"
set aRtext(buttonCANCEL) "Cancel"
set aRtext(buttonHELP)   "Help"

## For '.fRtexttemps' frame:

set aRtext(labelTEXTfahr) "Fahrenheit:"
set aRtext(labelTEXTcent) "    Centigrade:"
set aRtext(labelTEXTkelv) "        Kelvin:"

## For '.fRsliders' sub-frames:

set aRtext(labelSCALEfahr) "Fahrenheit:"
set aRtext(labelSCALEcent) "Centigrade:"
set aRtext(labelSCALEkelv) "Kelvin:"

## END OF  if { "$VARlocale" == "en"


##+########################################################################
##
##  GET INPUT PARM VALUE -- 
##  i.e. set VARcent from
##
##         1) an argument passed to this script
##      OR 
##         2)  environment var VAR_CENTIGRADE
##
##+########################################################################
##+########################################################################
##  Example argc/argv processing:
##
## if {$argc == 0} {
##    set VARtext "$env(CONFIRM_TEXT)"   
## } else {
##    set VARtext [lindex $argv 0]
## }
##+########################################################################

if {$argc == 1} {
   set VARcent [lindex $argv 0]
} else {
   set VARcent 0
   catch { set VARcent "$env(VAR_CENTIGRADE)" }
}

set VARfahr [expr {(9.0 * $VARcent / 5.0) + 32}]
set VARkelv [expr {$VARcent + 273.15}]


##+####################################################################
## DEFINE *ALL* THE FRAMES:
##   TOP-LEVEL FRAMES:
##     - 'fRbuttons'   - to contain several buttons (UseIt,Cancel,Help) 
##     - 'fRtexttemps' - to contain 3 pairs of label-and-text widgets.
##     - 'fRinfo'      - to contain a label widget.
##     - 'fRsliders'   - to contain 3 sliderbars, with 3 labels.
##
##  'fRsliders' is packed below the 'fRbuttons' frame.
##
##   SUB-FRAMES: (to contain each of the 3 scale widgets)
##     - 'fRsliders.fRfahr'
##     - 'fRsliders.fRcent'
##     - 'fRsliders.fRkelv'
##
##+####################################################################

## FOR TESTING:  (like expansion of frames, during window expansion)
# set RELIEF_frame raised
# set BDwidth_frame 2

set RELIEF_frame flat
set BDwidth_frame 0


frame .fRbuttons   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

# frame .fRtexttemps -relief raised  -borderwidth 2
frame .fRtexttemps -relief $RELIEF_frame  -borderwidth $BDwidth_frame

frame .fRinfo      -relief $RELIEF_frame  -borderwidth $BDwidth_frame

frame .fRsliders  -relief $RELIEF_frame  -borderwidth $BDwidth_frame

frame .fRsliders.fRfahr -relief raised  -borderwidth 2
frame .fRsliders.fRcent -relief raised  -borderwidth 2
frame .fRsliders.fRkelv -relief raised  -borderwidth 2


##+########################################################
## PACK *ALL* the FRAMES.
##+########################################################

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

pack  .fRtexttemps \
   -side top \
   -anchor nw \
   -fill x \
   -expand 1

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

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

## Pack the sub-frames.

pack .fRsliders.fRfahr \
     .fRsliders.fRcent \
     .fRsliders.fRkelv \
   -side left \
   -anchor center \
   -fill y \
   -expand 1


##+################################################################
## The frames are now defined and packed.
##+################################################################
## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES. 
##+################################################################
##+################################################################


##+########################################################
## IN THE 'fRbuttons' frame -- DEFINE several BUTTONS ---
## 1 'UseIt' BUTTON, 1 'Cancel' BUTTON, 1 'Help' BUTTON.
##  THEN PACK THE WIDGETS.
##+########################################################

button .fRbuttons.buttUSEIT \
   -text "$aRtext(buttonUSEIT)" \
   -font fontTEMP_button \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {put_vars}
 
button .fRbuttons.buttCANCEL \
   -text "$aRtext(buttonCANCEL)" \
   -font fontTEMP_button \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {exit}

button .fRbuttons.buttHELP \
   -text "$aRtext(buttonHELP)" \
   -font fontTEMP_button \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {popup_msgVarWithScroll .topHelp "$HELPtext"}

## Pack the widgets in frame 'fRbuttons'.

pack .fRbuttons.buttUSEIT \
     .fRbuttons.buttCANCEL \
     .fRbuttons.buttHELP \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+########################################################
## IN THE 'fRtexttemps' frame -- DEFINE
## 3 'TEXT' WIDGETS (rather than a 'label' or 'message' widget,
## so that it is possible to paste the text values
## to another window). Each text widget with a label widget.
## THEN PACK THE WIDGETS.
##+########################################################

## Provide label-and-text widgets for the temperature values
## in several different units.

set widthTextWidgetChars 10
set textWidgetFiller "          "

## FAHRENHEIT:

label .fRtexttemps.labTEXTfahr \
   -text "$aRtext(labelTEXTfahr)" \
   -font fontTEMP_label \
   -justify left \
   -anchor w \
   -relief flat \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -bd $BDwidthPx_label

text .fRtexttemps.txtVARfahr \
   -relief raised \
   -borderwidth $BDwidthPx_text \
   -height 1 \
   -width $widthTextWidgetChars \
   -wrap none \
   -font fontTEMP_text


## CENTIGRADE:

label .fRtexttemps.labTEXTcent \
   -text "$aRtext(labelTEXTcent)" \
   -font fontTEMP_label \
   -justify left \
   -anchor w \
   -relief flat \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -bd $BDwidthPx_label

text .fRtexttemps.txtVARcent \
   -relief raised \
   -borderwidth $BDwidthPx_text \
   -height 1 \
   -width $widthTextWidgetChars \
   -wrap none \
   -font fontTEMP_text


## KELVIN:

label .fRtexttemps.labTEXTkelv \
   -text "$aRtext(labelTEXTkelv)" \
   -font fontTEMP_label \
   -justify left \
   -anchor w \
   -relief flat \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -bd $BDwidthPx_label

text .fRtexttemps.txtVARkelv \
   -relief raised \
   -borderwidth $BDwidthPx_text \
   -height 1 \
   -width $widthTextWidgetChars \
   -wrap none \
   -font fontTEMP_text


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

pack .fRtexttemps.labTEXTfahr \
     .fRtexttemps.txtVARfahr \
     .fRtexttemps.labTEXTcent \
     .fRtexttemps.txtVARcent \
     .fRtexttemps.labTEXTkelv \
     .fRtexttemps.txtVARkelv \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+########################################################
## IN THE 'fRinfo' frame -- DEFINE 
## a LABEL widget.
##+########################################################

## The text for this button will be inserted as the
## sliders are moved, by the 'get_centitext' proc below.

label .fRinfo.labelINFO \
   -text "" \
   -font fontTEMP_label \
   -justify center \
   -anchor center \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -relief flat \
   -bd $BDwidthPx_label


pack .fRinfo.labelINFO \
   -side left \
   -anchor w \
   -fill x \
   -expand 1


##+########################################################
## Prepare some variables for use in defining the
## 3 scales below.
##+########################################################

set scaleLenPx 350
set scaleThickPx 10

set KELVmin 0
set KELVmax 4000.0

set CENTmin [expr {$KELVmin - 273.15}]
set CENTmax [expr {$KELVmax - 273.15}]

set FAHRmin [expr {(9.0 * $CENTmin / 5.0) + 32.0}]
set FAHRmax [expr {(9.0 * $CENTmax / 5.0) + 32.0}]

# set resolution 0.1
# set digits 5

set resolution 1
set digits 4


##+########################################################
## IN THE 'fRsliders.fRfahr' frame -- DEFINE 
## a LABEL widget and a SCALE widget.
## The scale is oriented vertically.
## THEN PACK THEM --- the label above the scale.
##+########################################################

## FAHRENHEIT:

label .fRsliders.fRfahr.label \
   -text "$aRtext(labelSCALEfahr)" \
   -font fontTEMP_label \
   -width 9 \
   -justify left \
   -anchor w \
   -relief flat \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -bd $BDwidthPx_label

scale .fRsliders.fRfahr.scale \
   -orient vertical \
   -from $FAHRmax -to $FAHRmin \
   -resolution $resolution \
   -digits $digits \
   -length $scaleLenPx \
   -variable VARfahr \
   -font fontTEMP_scale \
   -showvalue true \
   -bd $BDwidthPx_scale \
   -width $scaleThickPx

##   -command {temperature_update}

.fRsliders.fRfahr.scale set $VARfahr

##+#################################################
## Pack the widgets in the frame '.fRsliders.fRfahr'.
##+#################################################

pack .fRsliders.fRfahr.label \
   -side top \
   -anchor nw \
   -fill none \
   -expand 0

pack .fRsliders.fRfahr.scale \
   -side top \
   -anchor nw \
   -fill y \
   -expand 1


##+########################################################
## IN THE 'fRsliders.fRcent' frame -- DEFINE 
## a LABEL widget and a SCALE widget.
## The scale is oriented vertically.
## THEN PACK THEM --- the label above the scale.
##+########################################################

## CENTIGRADE:

label .fRsliders.fRcent.label \
   -text "$aRtext(labelSCALEcent)" \
   -font fontTEMP_label \
   -width 9 \
   -justify left \
   -anchor w \
   -relief flat \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -bd $BDwidthPx_label

scale .fRsliders.fRcent.scale \
   -orient vertical \
   -from $CENTmax -to $CENTmin \
   -resolution $resolution \
   -digits $digits \
   -length $scaleLenPx \
   -variable VARcent \
   -font fontTEMP_scale \
   -showvalue true \
   -bd $BDwidthPx_scale \
   -width $scaleThickPx

##   -command {temperature_update}

.fRsliders.fRcent.scale set $VARcent

##+#################################################
## Pack the widgets in the frame '.fRsliders.fRcent'.
##+#################################################

pack .fRsliders.fRcent.label \
   -side top \
   -anchor nw \
   -fill none \
   -expand 0

pack .fRsliders.fRcent.scale \
   -side top \
   -anchor nw \
   -fill y \
   -expand 1


##+########################################################
## IN THE 'fRsliders.fRkelv' frame -- DEFINE 
## a LABEL widget and a SCALE widget.
## The scale is oriented vertically.
## THEN PACK THEM --- the label above the scale.
##+########################################################

## KELVIN:

label .fRsliders.fRkelv.label \
   -text "$aRtext(labelSCALEkelv)" \
   -font fontTEMP_label \
   -width 9 \
   -justify left \
   -anchor w \
   -relief flat \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -bd $BDwidthPx_label

scale .fRsliders.fRkelv.scale \
   -orient vertical \
   -from $KELVmax -to $KELVmin \
   -resolution $resolution \
   -digits $digits \
   -length $scaleLenPx \
   -variable VARkelv \
   -font fontTEMP_scale \
   -showvalue true \
   -bd $BDwidthPx_scale \
   -width $scaleThickPx

##   -command {temperature_update}

.fRsliders.fRkelv.scale set $VARkelv


##+#################################################
## Pack the widgets in the frame '.fRsliders.fRkelv'.
##+#################################################

pack .fRsliders.fRkelv.label \
   -side top \
   -anchor nw \
   -fill none \
   -expand 0

pack .fRsliders.fRkelv.scale \
   -side top \
   -anchor nw \
   -fill y \
   -expand 1


##+#######################################
## END OF MAIN SECTION TO SETUP THE GUI.
## FRAMES AND WIDGETS ARE DEFINED.
##+#######################################

##+#######################################################################
##+#######################################################################
## BINDINGS section: 3 bindings on button1-release on the sliders (scales).
##+#######################################################################
##+#######################################################################

bind .fRsliders.fRfahr.scale  <ButtonRelease-1>  \
   {set TYPEchanged "FAHR" ; temperature_update 0}

bind .fRsliders.fRcent.scale  <ButtonRelease-1>  \
   {set TYPEchanged "CENT" ; temperature_update 0}

bind .fRsliders.fRkelv.scale  <ButtonRelease-1>  \
   {set TYPEchanged "KELV" ; temperature_update 0}


##+#####################################################################
##+#####################################################################
## PROCS section:
##    'temperature_update'     - called by button1-release bindings
##    'put_vars'               - called by the 'UseIt' button
##    'popup_msgVarWithScroll' - called by the 'Help' button
##+#####################################################################
##+#####################################################################


##+#####################################################################
## proc 'temperature_update'
##+##################################################################### 
## PURPOSE: As any of the 3 sliders is changed, set 
##          1) the other 2 sliders, and
##          2) update the text widgets that hold the temperature in 3
##             different units, in the 'fRbuttons' frame. 
##          3) update the text in the label of the 'fRinfo' frame.
##          4) Reset the color-palette of the window --- between
##             blue and red and white --- according to the Kelvin
##             value between the min and max Kelvin values.
##
## CALLED BY:  button1-release on 3 scale widgets:
##               - .fRsliders.fRfahr.scale
##               - .fRsliders.fRcent.scale
##               - .fRsliders.fRkelv.scale
##+#####################################################################

proc temperature_update {x} {

   global TYPEchanged VARfahr VARcent VARkelv \
      widthTextWidgetChars textWidgetFiller KELVmax

   ########################################################
   ## Reset the scale values of 2 scales from another scale.
   ########################################################

   if {"$TYPEchanged" == "FAHR"} {
      set VARfahr [.fRsliders.fRfahr.scale  get]
      set VARcent [expr {5.0 * ($VARfahr - 32.0) / 9.0}]
      set VARkelv [expr {$VARcent + 273.15}]
   }

   if {"$TYPEchanged" == "CENT"} {
      set VARcent [.fRsliders.fRcent.scale  get]
      set VARkelv [expr {$VARcent + 273.15}]
      set VARfahr [expr {(9.0 * $VARcent / 5.0) + 32.0}]
   }

   if {"$TYPEchanged" == "KELV"} {
      set VARkelv  [.fRsliders.fRkelv.scale get]
      set VARcent  [expr {$VARkelv - 273.15}]
      set VARfahr  [expr {(9.0 * $VARcent / 5.0) + 32.0}]
   }


   ######################################################
   ## Update the text numbers in the 3 text widgets.
   ######################################################

   ## Show the current temperature in FAHRENHEIT in a small text widget.
   ## Right-justify the number in the text widget.

   .fRtexttemps.txtVARfahr delete 1.0 end
   .fRtexttemps.txtVARfahr insert 1.0 "$textWidgetFiller"
   set fmtVARfahr [format "%5.2f" $VARfahr]
   set insertIDX [expr {$widthTextWidgetChars - [string length "$fmtVARfahr"]}]
   .fRtexttemps.txtVARfahr insert 1.$insertIDX $fmtVARfahr
   .fRtexttemps.txtVARfahr delete 1.[expr {$insertIDX + 7}] end

   ## FOR TESTING:
   #   puts "insertIDX: $insertIDX"

   ## Show the current temperature in CENTIGRADE in a small text widget.
   ## Right-justify the number in the text widget.

   .fRtexttemps.txtVARcent delete 1.0 end
   .fRtexttemps.txtVARcent insert 1.0 "$textWidgetFiller"
   set fmtVARcent [format "%5.2f" $VARcent]
   set insertIDX [expr {$widthTextWidgetChars - [string length "$fmtVARcent"]}]
   .fRtexttemps.txtVARcent insert 1.$insertIDX $fmtVARcent
   .fRtexttemps.txtVARcent delete 1.[expr {$insertIDX + 7}] end

   ## FOR TESTING:
   #   puts "insertIDX: $insertIDX"

   ## Show the current temperature in KELVIN in a small text widget.
   ## Right-justify the number in the text widget.

   .fRtexttemps.txtVARkelv delete 1.0 end
   .fRtexttemps.txtVARkelv insert 1.0 "$textWidgetFiller"
   set fmtVARkelv [format "%5.2f" $VARkelv]
   set insertIDX [expr {$widthTextWidgetChars - [string length "$fmtVARkelv"]}]
   .fRtexttemps.txtVARkelv insert 1.$insertIDX $fmtVARkelv
   .fRtexttemps.txtVARkelv delete 1.[expr {$insertIDX + 7}] end

   ## FOR TESTING:
   #   puts "insertIDX: $insertIDX"


   ######################################################
   ## Update the text in the label of the 'fRinfo' frame.
   ######################################################

   .fRinfo.labelINFO configure -text "[get_centitext]"


   ######################################################
   ## Set a temperature color --- and use it to set
   ## the color of some part of the GUI --- such as the
   ## troughs of the scale widgets (or the color-palette
   ## of the entire GUI window).
   ##
   ## The color is set via the 'set_temperatureColorformat'
   ## proc which returns a hex-color according to the Kelvin
   ## value between the min and max Kelvin values.
   ######################################################

   set hexCOLORpal [set_temperatureColor $VARkelv]

   # tk_setPalette $hexCOLORpal

   .fRsliders.fRfahr.scale configure -troughcolor $hexCOLORpal
   .fRsliders.fRcent.scale configure -troughcolor $hexCOLORpal
   .fRsliders.fRkelv.scale configure -troughcolor $hexCOLORpal

}
## END of proc 'temperature_update'


##+#################################################################
## PROCEDURE -- set_temperatureColor
##
## PURPOSE:  Returns a hex-color according to the Kelvin
##           value between the min and max Kelvin values.
##
## METHOD: The goal of the technique used here is to
##         basically go from blue to red (and then white)
##         as the Kelvin number goes from 0 to KELVmax.
##         In other words, go from cold-blue to red-hot
##         and white-hot.
##
##         If a Tcl-Tk coder wanted, they could change
##         the interpolation technique used --- and
##         the color spectrum used.         
##
## CALLED BY: the 'temperature_update' proc
##+################################################################

proc set_temperatureColor {VARkelv} {

   global KELVmax

   if {0} {
      ######################################################
      ## COLOR-SETTING METHOD 1:
      ## We will set the color according to VARkelv in 2 ranges:
      ## - between 0 and about 373 (100+273) - (blue to red)
      ## - between about 373 and KELVmax - (red to white).
      ##
      ##------------------------------------------------------
      ## For VARkelv between 0 and about 273+100=373 (boiling water):
      ##
      ## We make the BLUE component to go from 255 to 0 as
      ## VARkelv goes from 0 to 373
      ## and
      ## we make the RED component to go from 0 to 255 as
      ## VARkelv goes from 0 to 373.
      ## (We keep the GREEN component fixed near zero as
      ##  VARkelv is between 0 and 373.)
      ##
      ##------------------------------------
      ## For VARkelv between 373 and KELVmax:
      ##
      ## We keep the RED component at 255
      ## and
      ## we make the GREEN and BLUE components go from
      ## (near) 0 to 255 as VARkelv goes from 373 to KELVmax.
      ######################################################

      # set midHOTkelv 373
      set midHOTkelv 473

      if {$VARkelv < $midHOTkelv} {
         set Rpal255 [expr {int(255 * $VARkelv / $midHOTkelv)}]
         set Gpal255 0
         # set Gpal255 50
         set Bpal255 [expr {int(255 * ($midHOTkelv - $VARkelv) / $midHOTkelv)}]
      } else {
         set Rpal255 255
         set Gpal255 [expr {int(255 * ($VARkelv - $midHOTkelv) / $KELVmax)}]
         set Bpal255 $Gpal255
      }

   }
   ## END OF  if {0/1} section, for color-setting method 1


   if {1} {
      ######################################################
      ## COLOR-SETTING METHOD 2:
      ## We will set the color according to VARkelv in 3 ranges:
      ## - between 0 and about 373 (100+273) - (blue to red),
      ## - between about 373 and about 2000 - (red to yellow),
      ## - between about 2000 and KELVmax - (yellow to white).
      ##
      ##------------------------------------------
      ## For VARkelv between 0 and REDkelv (~473):
      ##
      ## We make the BLUE component to go from 255 to 0 as
      ## VARkelv goes from 0 to REDkelv
      ## and
      ## we make the RED component to go from 0 to 255 as
      ## VARkelv goes from 0 to REDkelv.
      ## (We keep the GREEN component fixed near zero as
      ##  VARkelv is between 0 and 373.)
      ##
      ##--------------------------------------------
      ## For VARkelv between REDkelv and YELLOWkelv (~2000):
      ##
      ## We keep the BLUE component at 0 and keep RED at 255 as
      ## we make the GREEN component go from 0 to 255 while
      ## VARkelv goes from REDkelv to YELLOWkelv.
      ##
      ##--------------------------------------------
      ## For VARkelv between YELLOWkelv and KELVmax:
      ##
      ## We keep the RED component at 255 and the GREEN component
      ## at 255 as we make the BLUE component go from
      ## 0 to 255 as VARkelv goes from YELLOWkelv to KELVmax.
      ######################################################

      set REDkelv 473
      # set YELLOWkelv 1000
      set YELLOWkelv [expr {int($KELVmax/2)}]
      set REDtoYELLOW [expr {$YELLOWkelv - $REDkelv}]
      set YELLOWtoMAX [expr {$KELVmax - $YELLOWkelv}]

      if {$VARkelv < $REDkelv} {
         set Rpal255 [expr {int(255 * $VARkelv / $REDkelv)}]
         set Gpal255 0
         # set Gpal255 50
         set Bpal255 [expr {int(255 * ($REDkelv - $VARkelv) / $REDkelv)}]
      } elseif {$VARkelv >= $REDkelv && $VARkelv <= $YELLOWkelv} {
         set Rpal255 255
         set Gpal255 [expr {int(255 * ($VARkelv - $REDkelv) / $REDtoYELLOW)}]
         set Bpal255 0
      } else {
         set Rpal255 255
         set Gpal255 255
         set Bpal255 [expr {int(255 * ($VARkelv - $YELLOWkelv) / $YELLOWtoMAX)}]
      }

   }
   ## END OF  if {0/1} section, for color-setting method 2

   return [format "#%02X%02X%02X" $Rpal255 $Gpal255 $Bpal255]
}
## END of proc 'set_temperatureColor'


##+#################################################################
## PROCEDURE -- get_centitext
##
## PURPOSE:  Update the text in the label of the 'fRinfo' frame
##           with some temperature info related to the temperature
##           in the variable VARcent.
##
## CALLED BY: the 'temperature_update' proc
##+################################################################

proc get_centitext {} {

   global LISTcentipairs aRcentitext VARcent

   ## FOR TESTING: (temporarily disable this proc)
   # return "This is a test."

   ###############################################################
   ## Get the 'centipair' of list 'LISTcentipairs' that 'embraces'
   ## the current value of VARcent --- and use that 'pair index'
   ## to get a text string from array 'aRcentipairs'.
   ###############################################################
   ## It would be more efficient to do some sort of binary
   ## search in the pairs-list, but, for now, we simply do
   ## a linear-search from one end of the list to the other.
   ###############################################################

   foreach pair $LISTcentipairs {
      foreach {min max} $pair {break}
      if {$VARcent >= $min && $VARcent <= $max} {
         return "$aRcentitext($min,$max)"
      }
   }
   ## END OF foreach pair ...

}
## END of proc 'get_centitext'


##+#####################################################################
## PROCEDURE -- put_vars
##
## PURPOSE: Puts a string containing the 3 temperatures
##          (VARfahr, VARcent, VARkelv)
##          to standard output. Then exits this GUI.
##
## Called by:  button .fRbuttons.buttOK
##+#####################################################################

proc put_vars { } {

   global VARfahr VARcent VARkelv

   puts "$VARfahr $VARcent $VARkelv"

   exit

}
## END of proc  'puts_vars'

  
##+########################################################################
## PROC 'popup_msgVarWithScroll'
##+########################################################################
## PURPOSE: Report help or error conditions to the user.
##
##       We do not use focus,grab,tkwait in this proc,
##       because we use it to show help when the GUI is idle,
##       and we may want the user to be able to keep the Help
##       window open while doing some other things with the GUI
##       such as putting a filename in the filename entry field
##       or clicking on a radiobutton.
##
##       For a similar proc with focus-grab-tkwait added,
##       see the proc 'popup_msgVarWithScroll_wait' in a
##       3DterrainGeneratorExaminer Tk script.
##
## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk',
##            4th edition, by Welch, Jones, Hobbs.
##
## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg)
##            and a variable holding text (many lines, if needed).
##
## CALLED BY: 'help' button
##+########################################################################
## To have more control over the formatting of the message (esp.
## words per line), we use this 'toplevel-text' method, 
## rather than the 'tk_dialog' method -- like on page 574 of the book 
## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications
## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor".
##+########################################################################

proc popup_msgVarWithScroll { toplevName VARtext } {

   ## global fontTEMP_text fontTEMP_button  ;# Not needed. 'wish' makes this global.
   ## global env               ;# Could be used to personalize the window title.

   ## Using the 'bell' would probably be annoying, if it worked.
   # bell
   # bell
  
   #################################################
   ## Set VARwidth & VARheight from $VARtext.
   #################################################
   ## To get VARheight,
   ##    split at '\n' (newlines) and count 'lines'.
   #################################################
 
   set VARlist [ split $VARtext "\n" ]

   ## For testing:
   #  puts "VARlist: $VARlist"

   set VARheight [ llength $VARlist ]

   ## For testing:
   #  puts "VARheight: $VARheight"


   #################################################
   ## To get VARwidth,
   ##    loop through the 'lines' getting length
   ##     of each; save max.
   #################################################

   set VARwidth 0

   #############################################
   ## LOOK AT EACH LINE IN THE LIST.
   #############################################
   foreach line $VARlist {

      #############################################
      ## Get the length of the line.
      #############################################
      set LINEwidth [ string length $line ]

      if { $LINEwidth > $VARwidth } {
         set VARwidth $LINEwidth 
      }

   }
   ## END OF foreach line $VARlist

   ## For testing:
   #   puts "VARwidth: $VARwidth"


   ###############################################################
   ## NOTE: VARwidth works for a fixed-width font used for the
   ##       text widget ... BUT the programmer may need to be
   ##       careful that the contents of VARtext are all
   ##       countable characters by the 'string length' command.
   ###############################################################


   #####################################
   ## SETUP 'TOP LEVEL' HELP WINDOW.
   #####################################

   catch {destroy $toplevName}
   toplevel  $toplevName

   # wm geometry $toplevName 600x400+100+50

   wm geometry $toplevName +100+50

   wm title     $toplevName "Note"
   # wm title   $toplevName "Note to $env(USER)"

   wm iconname  $toplevName "Note"


   #####################################
   ## In the frame '$toplevName' -
   ## DEFINE THE TEXT WIDGET and
   ## its two scrollbars --- and
   ## DEFINE an OK BUTTON widget.
   #####################################

   if {$VARheight > 10} {
      text $toplevName.text \
         -wrap none \
         -font fontTEMP_text \
         -width  $VARwidth \
         -height $VARheight \
         -bg "#f0f0f0" \
         -fg "#000000" \
         -relief raised \
         -bd 2 \
         -yscrollcommand "$toplevName.scrolly set" \
         -xscrollcommand "$toplevName.scrollx set"

      scrollbar $toplevName.scrolly \
         -orient vertical \
         -command "$toplevName.text yview"

      scrollbar $toplevName.scrollx \
         -orient horizontal \
         -command "$toplevName.text xview"
   } else {
      text $toplevName.text \
         -wrap none \
         -font fontTEMP_text \
         -width  $VARwidth \
         -height $VARheight \
         -bg "#f0f0f0" \
         -fg "#000000" \
         -relief raised \
         -bd 2 
   }

   button $toplevName.butt \
      -text "OK" \
      -font fontTEMP_button \
      -command  "destroy $toplevName"

   ###############################################
   ## PACK *ALL* the widgets in frame '$toplevName'.
   ###############################################

   ## Pack the bottom button BEFORE the
   ## bottom x-scrollbar widget,

   pack  $toplevName.butt \
      -side bottom \
      -anchor center \
      -fill none \
      -expand 0


   if {$VARheight > 10} {
      ## Pack the scrollbars BEFORE the text widget,
      ## so that the text does not monopolize the space.

      pack $toplevName.scrolly \
         -side right \
         -anchor center \
         -fill y \
         -expand 0

      ## DO NOT USE '-expand 1' HERE on the Y-scrollbar.
      ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS
      ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA.
                
      pack $toplevName.scrollx \
         -side bottom \
         -anchor center \
         -fill x  \
         -expand 0

      ## DO NOT USE '-expand 1' HERE on the X-scrollbar.
      ## THAT KEEPS THE TEXT AREA FROM EXPANDING.

      pack $toplevName.text \
         -side top \
         -anchor center \
         -fill both \
         -expand 1
   } else {
      pack $toplevName.text \
         -side top \
         -anchor center \
         -fill both \
         -expand 1
   }


   #####################################
   ## LOAD MSG INTO TEXT WIDGET.
   #####################################

   ##  $toplevName.text delete 1.0 end
 
   $toplevName.text insert end $VARtext
   
   $toplevName.text configure -state disabled
  
}
## END OF PROC 'popup_msgVarWithScroll'


##+###############################################
## END OF PROCS SECTION.
##+###############################################
## Setting of HELPtext variable follows.
##+###############################################

set HELPtext "\
*** HELP for this tkTemperature-Convert-Select GUI ****

This TkGUI script provides a GUI for showing temperatures
between absolute zero and several thousand degrees ---
in three different units:

      - Fahrenheit
      - Centigrade
      - Kelvin.

This utility is a 'converter-selector' in the sense that

   1) You can convert a number from any of the three temperature
      scales to the other two by simply moving any one of the
      three 'slider buttons'.

   2) You can click on the 'UseIt' button to return the
      current 3 numbers --- for a temperature in Fahrenheit,
      Centigrade, and Kelvin --- to a calling application,
      such as a shell script or another Tk script.


****************************
THE CONVERSION FUNCTIONALITY of this utility:

The 3 different temperature values (in different units) are shown
(and selected for conversion) via 3 'sliders' of 3 Tk 'scale' widgets. 

These 3 different (corresponding) temperature numbers are also
shown in 3 'text' widgets, several characters wide --- from which
a user can copy-and-paste the numbers from this GUI window to
some other window. 

For any 'slider' change, this GUI script IMMEDIATELY (when the mouse
button is released) updates the location of the other 2 'sliders'.

The 3 text widgets, showing the temperature in the 3 different
units, are also 'immediately' updated whenever any one of the 3
scale widget 'sliders' are changed.

***************************
FINE CONTROL OF THE SLIDERS:

Most people know that you can drag the sliders by clicking
on a 'slider button' with mouse-button1 and dragging the
slider button. This is a rather coarse way of moving the
slider button.

It is not so obvious that the Tk scale widget also allows
you to move the 'slider button' by clicking in the 'trough'
on EITHER SIDE of the slider button. By repeated clicking
in the trough, the slider can be advanced one scale
resolution-unit per click.

Furthermore, one can RAPIDLY move the slider button,
in a fairly well-controlled fashion, by clicking in the
'trough' and HOLDING down the mouse button. The slider moves
one resolution unit at a time UNTIL the mouse button is
RELEASED.

**************************
THE SELECTOR FUNCTIONALITY:

By clicking on a 'UseIt' button on the GUI, this script
returns a string containing the current temperature (in the
3 different units) to a calling script/application.

When using this script in another script/program,
this script will accept a temperature (in Centigrade) as an
argument which is used to initialize the
      - setting of the 3 'sliders', and
      - the numbers shown in the 3 small text widgets.

*************************
SOME USES of this utility:

   1) Useful for helping math-science students get a visual
      'feel' for the relation between Fahrenheit, Centigrade,
      and Kelvin.

   2) Also could be useful to programmers (for example,
      Tcl-Tk programmers) for determining temperatures
      (example: in Centigrade) to be used in various (Tcl-Tk) apps.

   3) IN A SHELL SCRIPT or ANOTHER TK SCRIPT, this Tk script can
      ACT AS AN TEMPERATURE SELECTOR by passing the current temperature
      (in the 3 different units) to stdout, when the 'UseIt' button
      is clicked.
            Example output string:  32.0  0  273.0

                           (Fahrenheit,Centigrade,Kelvin)


***************************
SOME REFERENCE TEMPERATURES:

The following table gives some 'temperature transition points',
in degrees Centigrade, for various materials.

    ---------------------------------------------------

    The string 'solid <-> liquid' can be read
    'changes between solid and liquid'.

    This can mean 'melts' or 'freezes' depending on
    whether you think of the temperature increasing or
    decreasing (heat being added/removed), respectively.

    So 'solid <-> liquid' can also be thought of as
    'melts/freezes' --- but it may be strange
    to think of freezing at high temperatures.

    ----------------------------------------------------

    The string 'liquid <-> gas' can be read
    'changes between liquid and gas'.

    This can mean 'boils' or 'condenses' depending on
    whether you think of the temperature increasing or
    decreasing (heat being added/removed), respectively.

    So 'liquid <-> gas' can also be thought of as
    'boils/condenses'.

    ----------------------------------------------------

Temperature
in Centigrade       Phenomenon (at 'atmospheric pressure')
-----------------   -----------------------------------------

at 3422 C           tungsten solid <-> liquid (melts/freezes)

at 3287 C           titanium liquid <-> gas  (boils/condenses)

at 2807 C           gold     liquid <-> gas  (boils/condenses)

at 1772 C           platinum solid <-> liquid (melts/freezes)

at 1667 C           titanium solid <-> liquid   (melts/freezes)

at 1535 C           iron     solid <-> liquid   (melts/freezes)

at 1400 to 1500 C   steel    solid <-> liquid   (melts/freezes)

at 1250 to 1450 C   porcelain solid <-> liquid  (melts/freezes)

at 1083.4 C         copper   solid <-> liquid   (melts/freezes)

at 1064.43 C        gold     solid <-> liquid   (melts/freezes)

at ~600 to ~1400 C  clay glazes  solid <-> liquid    (melts/freezes)

at 900 to 1000 C    brass(copper+zinc)  solid <-> liquid  (melts/freezes)

at  881 C           sodium    liquid <-> gas   (boils/condenses)

at  766 C           potassium  liquid <-> gas   (boils/condenses)

at  444.6 C         sulfur    liquid <-> gas    (boils/condenses)

at  419.58 C        zinc      solid <-> liquid   (melts/freezes)

at 113 to 120 C     sulfur    solid <-> liquid   (melts/freezes)

at  100.0 C         water     liquid <-> gas    (boils/condenses)

at   97.8 C         sodium    solid <-> liquid  (melts/freezes)

at   63.2 C         potassium  solid <-> liquid  (melts/freezes)

37 C                is normal human body temperature 

about 25 C          is 'room temperature'

at 0.0 C            ice/water/H2O       solid <-> liquid   (melts/freezes)

at -56.6 C          carbon-dioxide/CO2  liquid <-> gas   (boils/condenses)

at -78.5 C          carbon-dioxide/CO2  solid <-> liquid  (melts/freezes)

at -152.3 C         krypton     liquid <-> gas    (boils/condenses)

at -156.6 C         krypton     solid <-> liquid   (melts/freezes)

at -182.9 C         oxygen/O2   liquid <-> gas    (boils/condenses)

at -195.8 C         nitrogen/N2  liquid <-> gas    (boils/condenses)

at -209.9 C         nitrogen/N2  solid <-> liquid   (melts/freezes)

at -218.4 C         oxygen/O2    solid <-> liquid   (melts/freezes)

at -259 C           hydrogen/H2  solid <-> liquid   (melts/freezes)

-273.15 C           is absolute zero   

Some 'freaks':

Arsenic, dry ice (solid carbon dioxide), and iodine go directly
from solid to gas (sublimation) at normal atmospheric pressure.

***********
REFERENCES:

Some interesting information on temperatures can be seen at

http://en.wikipedia.org/wiki/Absolute_zero
http://en.wikipedia.org/wiki/Absolute_hot

and information on boiling points and melting points can be seen
at pages like

http://en.wikipedia.org/wiki/Tungsten
http://en.wikipedia.org/wiki/Carbon_dioxide
etc.
"


##+###############################################
## ADDITONAL GUI INITIALIZATION (if any) FOLLOWS.
##+###############################################
## See the setting of initial temps at the argv section
## near the top of this code --- near the frame definitions.
## (We allow the initial temperature to be passed as an
##  argument to this script.)
##+#########################################################

##+#########################################################
## Set the list 'LISTcentipairs' and the array 'aRcentitext'
## for use by the 'get_centitext' proc.
##+#########################################################
## The centigrade-temperature-pairs should cover the
## entire range --- from -300 to 4000, say --- with no gaps.
##
## That is, the left-number of one pair should match the
## right-number of the pair below, in this list.
##
## So you can add more entries to the list by 'splitting'
## a pair of numbers. For example: The statments
##
##  lappend LISTcentipairs "3300 4000"
##  set aRcentitext(3300,4000) "tungsten melts  at 3410 C"
##
##  lappend LISTcentipairs "3000 3300"
##  set aRcentitext(3000,3300) "titanium boils  at 3287 C"
## 
## could have been generated from the statement-pair
##
##  lappend LISTcentipairs "3000 4000"
##  set aRcentitext(3000,4000) "tungsten melts  at 3410 C"
##
## by inserting the new information about the titanium
## boiling point --- by adding the number 3300 between
## 3000 and 4000.
##+##########################################################

set maxInfoWidthChars 0

lappend LISTcentipairs "3300 4000"
set aRcentitext(3300,4000) "At 3422 C, tungsten  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(3300,4000)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "3000 3300"
set aRcentitext(3000,3300) "At 3287 C, titanium  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(3000,3300)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "2000 3000"
set aRcentitext(2000,3000) "At 2807 C, gold  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(2000,3000)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "1700 2000"
set aRcentitext(1700,2000) "At 1772 C, platinum  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(1700,2000)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "1600 1700"
set aRcentitext(1600,1700) "At 1667 C, titanium  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(1600,1700)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "1500 1600"
set aRcentitext(1500,1600) "At 1535 C, iron  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(1500,1600)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "1400 1500"
set aRcentitext(1400,1500) "At 1400 to 1500 C, steels  melt/freeze  (change between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(3300,4000)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "1200 1400"
set aRcentitext(1200,1400) "At 1250 to 1450 C, porcelain  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(1200,1400)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "1080 1200"
set aRcentitext(1080,1200) "At 1083.4 C, copper  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(1080,1200)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "1000 1080"
set aRcentitext(1000,1080) "At 1064.43 C, gold  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(1000,1080)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "950 1000"
set aRcentitext(950,1000) "At about 600 to 1400 C, clay glazes  melt/freeze  (change between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(950,1000)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "900 950"
set aRcentitext(900,950) "At 900 to 1000 C, brass(copper+zinc)  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(900,950)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "800 900"
set aRcentitext(800,900) "At  881 C, sodium  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(800,900)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "600 800"
set aRcentitext(600,800) "At  766 C, potassium  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(600,800)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "430 600"
set aRcentitext(430,600) "At  444.6 C, sulfur  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(430,600)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "300 430"
set aRcentitext(300,430) "At  419.58 C, zinc  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(300,430)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "110 300"
set aRcentitext(110,300) "At 113 to 120 C, sulfur  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(110,300)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "99 110"
set aRcentitext(99,110) "At 100.0 C, water/steam/H2O  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(99,110)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "80 99"
set aRcentitext(80,99) "At  97.8 C, sodium melts/freezes (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(80,99)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "50 80"
set aRcentitext(50,80) "At 63.2 C, potassium  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(50,80)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "30 50"
set aRcentitext(30,50) "37 C  is  normal human body temperature."

set tempWidthChars [string length "$aRcentitext(30,50)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "20 30"
set aRcentitext(20,30) "About 25 C  is  'room temperature'."

set tempWidthChars [string length "$aRcentitext(20,30)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-40 20"
set aRcentitext(-40,20) "At 0.0 C, ice/water/H2O  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(-40,20)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-70 -40"
set aRcentitext(-70,-40) "At -56.6 C, carbon-dioxide/CO2  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(-70,-40)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-120 -70"
set aRcentitext(-120,-70) "At -78.5 C, carbon-dioxide/CO2  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(-120,-70)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-155 -120"
set aRcentitext(-155,-120) "At -152.3 C, krypton  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(-155,-120)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-170 -155"
set aRcentitext(-170,-155) "At -156.6 C, krypton  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(-170,-155)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-190 -170"
set aRcentitext(-190,-170) "At -182.9 C, oxygen/O2  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(-190,-170)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-200 -190"
set aRcentitext(-200,-190) "At -195.8 C, nitrogen/N2  boils/condenses  (changes between liquid and gas)."

set tempWidthChars [string length "$aRcentitext(-200,-190)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-215 -200"
set aRcentitext(-215,-200) "At -209.9 C, nitrogen/N2  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(-215,-200)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-240 -215"
set aRcentitext(-240,-215) "At -218.4 C, oxygen/O2  melts/freezes  (changes between solid and liquid)."

set tempWidthChars [string length "$aRcentitext(-240,-215)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-270 -240"
set aRcentitext(-270,-240) "At -259 C, hydrogen/H2  melts/freezes  (changes between solid and liquid). "

set tempWidthChars [string length "$aRcentitext(-270,-240)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

lappend LISTcentipairs "-300 -270"
set aRcentitext(-300,-270) "-273.15 C  is  absolute zero."

set tempWidthChars [string length "$aRcentitext(-300,-270)"]
if {$tempWidthChars > $maxInfoWidthChars} {set maxInfoWidthChars $tempWidthChars}

## FOR TESTING:
# puts "LISTcentipairs: $LISTcentipairs"
# puts "aRcentitext(-300,-270): $aRcentitext(-300,-270)"

.fRinfo.labelINFO configure -width $maxInfoWidthChars


##+########################################
## Initialize the scale widgets, by calling
## the 'temperature_update' proc.
##+########################################

set TYPEchanged "CENT"
temperature_update $VARcent


SOME POTENTIAL ENHANCEMENTS:

Some might want another scale widget on the GUI --- for degrees Rankine, which is to Fahrenheit as Kelvin is to Centigrade.

---

I probably should add a 'ColorMe' button to this GUI --- like I did for a font-selector GUI whose code is at YAFSG - Yet Another Font Selector GUI and a tkPointerSettings GUI whose code I contributed at tkPointerSettings - a Tk GUI 'wrapper' for the 'xinput' command on this wiki.

With a 'ColorMe' button, the user could easily use a color selector, like the one at A non-obfuscated color selector GUI, to change the color of this tkTemperature-Convert-Select GUI.

Instead of supplying a 'ColorMe' button to do that, I simply point out that the user can change the RGB values for the 'tk_setPalette' statement near the top of this code.

---

More materials and their melting/boiling points could be added to this GUI, in a manner that is described at the bottom of the code --- where the 'LISTcentipairs' list and 'aRcentitext' array are built.

---

As I was putting finishing touches on this code, I kept finding some corrections/improvements to make in the melting/boiling point information. I have little doubt that there are some further corrections or improvements to be made.

Also, I have not tried to make the conversions consistently accurate to a couple of decimal places. I have been satisfied if the conversions are correct to the nearest integer degree. However, if others are not satisfied with that approach, this is where having Tcl-Tk script code at hand is a boon. Simply change the code however you like.


IN CONCLUSION

As I have said on quite a few other code-donation pages on this wiki ...

There's a lot to like about a utility that is 'free freedom' --- that is, no-cost and open-source so that you can modify/enhance/fix it without having to wait for someone else to do it for you (which may be never).

A BIG THANK YOU to Ousterhout for starting Tcl-Tk, and a BIG THANK YOU to the Tcl-Tk developers and maintainers who have kept the simply MAH-velous 'wish' interpreter going.