#!/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 ##+####################################################################### ## Tk SCRIPT NAME: tkAreaConvertSelect.tk ## ## WHERE: in $FEDIR_TKGUIS/SELECTORtools = $FEDIR/tkGUIs/SELECTORtools ## ## where $FEDIR is the installation directory ## of an FE subsystem --- such as 'tkGooies'. ## Reference: www.freedomenv.com ## ## Typically FEDIR is $HOME/apps/tkGooies_linux_yyyymmmdd ## where yyyymmmdd represents a release date such as 2017aug29 ##+####################################################################### ## PURPOSE: This Tk GUI script provides a GUI for CONVERTING AREA ## in one type of units to another type of units. ## ## The GUI allows for showing a single area in two ## different units of measure. ## ## The GUI allows the user to pick a pair of units from ## a list of units. Examples: acres, hectares, square-inches, ## square-feet, square-yards, square-miles, square-kilometers, ## square-meters, square-centimeters, square-millimeters, ## square-micrometers, square-nanometers, square-lightyears, ... ## ## This converter is intended to be able to convert areas chosen ## from a huge range of sizes: from the area of a cross-section of the ## 'observable' universe to the area of a cross-section of an atom or ## smaller. ## ## Estimates of the diameter of the 'observable' universe are on the ## order of 1.0 times 10 to the 11th lightyears. The AREA of a ## cross-section is approximately the square of that magnitude --- ## approximately 10 to the 22nd lightyears. ## ## Estimates of the diameter of a hydrogen atom is on the order of ## 1.0 times 10 to the -11 meters. The AREA of a cross-section is ## approximately the square of that magnitude --- approximately ## 10 to the -22 meters. And sub-atomic particles like an electron ## are at least a factor of 100 smaller. ## ## In order to be able to handle such huge ranges, in the various ## units, the GUI allows for specifying the area (in either ## of the pair of user-selected units) in 'scientific notation': ## a COEFFICIENT (say, between 1.00000000 and 1000.00000000) ## and ## an EXPONENT (for a 'base' of 10). ## ## And the exponents we want to be able to handle are on the order ## of about minus-100 --- to plus-100 --- including zero. ## ## SELECTION as well as CONVERSION: ## ## In addition to CONVERTING, this GUI is also for SELECTING ## the chosen area value --- by clicking on a 'UseIt' button. ## Both forms of the area, in the two user-selected units, ## are sent to 'stdout' --- along with an indicator of the ## 2 units of measure selected by the user. ## ## With this 'selection' capability, this Tk script can be called ## IN A SHELL SCRIPT OR ANOTHER TK SCRIPT, so that this Tk script ## can ACT AS AN AREA CONVERTER-SELECTOR utility. ## ##+################# ## THE GUI WIDGETS: ## ## The options available to the user are indicated by ## the following 'sketch' of the GUI: ## ## FRAMEnames ## VVVVVVVVVV ## ----------------------------------------------------------------------------------- ## tkArea - Converter/Selector ## [window title] ## ----------------------------------------------------------------------------------- ## ## .fRbuttons {UseIt} {Cancel} {Help} {Calc 2from1} {Clear2} {Calc 1from2} {Clear1} ## ## .fRmsg [.... Messages to user are display in a label here ...............................] ## ## .fRleft .fRright ## [The sub-frames below are [The scrollable listbox below ## in this '.fRleft' frame.] is in this '.fRright' frame.] ## ## |-------------------------A ## .fRleft.fRunits1 Units1:[selected units displayed here] O units1 | acres | ## [in a text widget] | hectares | ## .fRleft.fRvalues1 Coefficient: ___________ Exponent: {+}___{-} | square-inches | ## | square-feet | ## .fRleft.fRunits2 Units2:[selected units displayed here] O units2 | ... | ## [in a text widget] | square-kilometers | ## .fRleft.fRvalues2 Coefficient: ____________ Exponent: {+}___{-} | square-light-years | ## | ... | ## | square-meters | ## | square-miles | ## | square-nanometers | ## | square-yards | ## |<----------------------->V ## ## [We put the units-of-measure in the listbox in alphabetical order ## so that the user can relatively easily locate a unit-of-measure ## in what may eventually be a rather long list.] ## ------------------------------------------------------------------- ## ## In the above sketch of the GUI: ## ## SQUARE BRACKETS indicate a comment (not to be placed on the GUI). ## BRACES indicate a Tk 'button' widget. ## A COLON indicates that the text before the colon is on a 'label' widget. ## (To clarify the label extent, single quotes may be used around the text.) ## UNDERSCORES indicate a Tk 'entry' widget. ## CAPITAL-O indicates a Tk 'radiobutton' widget. ## CAPITAL-X indicates a Tk 'checkbutton' widget (if any). ## <----O----> indicates a Tk 'scale' widget (if any). ## ##+############## ## GUI components: ## ## From the GUI 'sketch' above, it is seen that the GUI consists of ## about ## ## - 11 button widgets ## - 7 label widgets ## - 4 entry widgets ## - 2 text widgets ## - 1 listbox widget ## - 2 radiobutton widgets (1 pair on 2 separate 'units' lines of the GUI) ## - 0 checkbutton widgets ## - 0 scale widgets ## ##+##################################################################### ## CALLED BY: a 'SELECTORtools' toolchest in the 'tkGooies' FE system ## OR ## in a shell script or another Tk script. ##+##################################################################### ## INPUTS: The user uses either of the 2 pairs of entry widgets ## to enter a coefficient and an exponent. In addition, the ## user sets 'units1' and 'units2' via radiobuttons and ## the listbox of the GUI. ## ## OUTPUT: The converted area values are displayed in a pair of ## entry widgets --- for computed coefficient and exponent. ## ## It the user clicks on the 'UseIt' button, ## the coefficient and exponent values are passed to stdout ## as a string giving area in the two user-selected units. ## ## Sample output string: ## square-inches 144.000000 0 square-feet 1.000000 0 ## ##+##################################################################### ## CALL FORMAT: ## ## .../tkAreaConvertSelect.tk [units1 coef1 exp1 units2] ## ## OR ## we could eventually ## use environment variables to initialize the entry and radiobutton widgets. ## ----------------------------------------------------------------------- ## EXAMPLE CALLS in a shell script: ## (Could also be called in a tcl-tk script.) ## ## 1) ## TEMP=`$FEDIR_TKGUIS/tkAreaConvertSelect.tk` ## or ## TEMP=`$FEDIR_TKGUIS/tkAreaConvertSelect.tk 6.0 1 square-inches square-centimeters` ## ## (The coef2 and exp2 values are computed from coef1 and exp1 and units1 ## in terms of units2.) ## ## OR if we implement intitializing the GUI via environment variables: ## we may do something like the following: ## ## 2) COEF1=6.0 ## EXP1=1 ## UNITS1="square-inches" ## UNITS2="square-centimeters" ## export COEF1 EXP1 UNITS1 UNITS2 ## ## TEMP=`$FEDIR_TKGUIS/tkAreaConvertSelect.tk` ## ## ## Note: ## The values in TEMP can be extracted with a command like 'cut' or 'awk' ## in a shell script --- or with 'string' commands in a Tcl-Tk script. ##+######################################################################## ## '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' , .fRmsg', '.fRleft' , '.fRright' ## ## Sub-frames: ## '.fRleft.fRunits1' ## '.fRleft.fRvalues1' ## '.fRleft.fRunits2' ## '.fRleft.fRvalues2' ## ## '.fRbuttons' and '.fRmsg' are to be on the top of the GUI, and ## '.fRleft' , '.fRright' are to be under '.fRmsg' ## on the left and right, respectively. ## ## 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: ## ## - '.fRbuttons' (to contain several button widgets) ## ## - '.fRmsg' (to contain a label widget) ## ## - '.fRleft.fRunits1' (to contain label, text, and radiobutton widgets) ## - '.fRleft.fRvalues1' (to contain 2 pairs of label and entry widgets) ## - '.fRleft.fRunits2' (to contain label, text, and radiobutton widgets) ## - '.fRleft.fRvalues2' (to contain 2 pairs of label and entry widgets) ## ## - 'fRright' (to contain a listbox widget) ## ## 3) Define BINDINGS: For example, on the listbox widget. ## See BINDINGS section below. ## ## 4) Define PROCS: ## ## - 'listbox_select_units' - called by a button1-release binding on ## the listbox widget. ## ## - 'area_update' - called by either of the 2 'Calc' buttons. ## ## - 'adjust_values1' - called by the '+' and '-' buttons for exp1. ## - 'adjust_values2' - called by the '+' and '-' buttons for exp2. ## ## - 'clear_values2' - called by the 'Clear2' button. ## - 'clear_values1' - called by the 'Clear1' button. ## ## - 'edit_inputs' - called by the 'area_update' proc. ## - 'decimal_check' - called by the 'edit_inputs' proc. ## ## - 'put_vars' - called by the 'UseIt' button. ## ## - 'advise_user' - called at bottom of script and could be ## called in one or more of the procs above. ## ## - 'popup_msgVarWithScroll' - called by the 'Help' button and by the ## 'edit_inputs' proc. ## ## 5) Additional GUI INITIALIZATION: initialize entry widgets and ## set the HELPtext var for the Help button. ## ##+####################################################################### ## DEVELOPED WITH: Tcl-Tk 8.5 on Ubuntu 9.10 (2009 october, 'Karmic Koala). ## ## wish> puts "$tcl_version $tk_version" ## showed ## 8.5 8.5 ##+######################################################################## ## FE system Copyright 2006+ by Blaise Montandon ##+######################################################################## ## MAINTENANCE HISTORY: ## Created by: Blaise Montandon 2017aug13 Started version for the FE 'tkGooies' ## system, on Linux. ## Changed by: Blaise Montandon 2017aug22 Put finishing touches on a version ## for release in 2017. ## Changed by: Blaise Montandon 2017aug28 Added some 'BDwidthPx_' and 'RELIEF_' ## variables for widget definitions. ## Changed the order of parameters in ## the widget definitions to basically ## group by FUNCTION (show text or define ## variable), FORMAT (width, height, font), ## GEOMETRY (pad & border parms), COLOR. ## Changed by: Blaise Montandon 2017aug30 Changed 'VARunits1' and 'VARunits2' to ## 'CHOSEunits1' and 'CHOSEunits2' to ## indicate these variables hold the units ## chosen by the user -- or hold the chosen ## initial default units. ## Changed by: Blaise Montandon 2017sep01 Changed four variable names in the ## 'area_update' proc to support added ## comments on derivation of the ## area conversion formulas. ##+############################################################################ ##+################################# ## SET THE TOP WINDOW NAME. ##+################################# wm title . "tkArea - Converter / Selector" wm iconname . "AreaConvert" # catch { wm title . "$env(FE_WIN_TITLE)" } # catch { wm iconname . "$env(FE_ICON_TITLE)" } ##+################################### ## SET THE TOP WINDOW POSITION. ##+################################### wm geometry . +50+50 # catch {eval wm geometry . "$env(FE_WIN_LOC_GEOM)" } ##+####################################################################### ## SET COLOR SCHEME (palette) FOR THE WINDOW. ##+####################################################################### if {1} { ## Grayish palette set Rpal255 210 set Gpal255 210 set Bpal255 210 } if {0} { ## Bluish palette set Rpal255 180 set Gpal255 180 set Bpal255 255 } if {0} { ## Greenish palette set Rpal255 180 set Gpal255 255 set Bpal255 180 } if {0} { ## Reddish palette set Rpal255 255 set Gpal255 180 set Bpal255 180 } set hexCOLORpal [format "#%02X%02X%02X" $Rpal255 $Gpal255 $Bpal255] tk_setPalette $hexCOLORpal ## Set color of background for some widgets. set msgBKGD "#ffcccc" set entryBKGD "#f0f0f0" set listboxBKGD "#f0f0f0" # set textBKGD "#f0f0f0" set textBKGD $msgBKGD # set radbuttBKGD "#c0c0c0" set radbuttBKGD $msgBKGD set radbuttSELECTCOLOR "#cccccc" # set chkbuttBKGD "#c0c0c0" # set scaleBKGD "#f0f0f0" ##+####################################################################### ## SET FONT 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_fixedwidth $FONT_fixedwidth eval font create fontTEMP_varwidth $FONT_varwidth 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_text $FONT_fixedwidth 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_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_text $FONT_SMALL_fixedwidth # 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 ##+####################################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. padx,pady for buttons) ## ## Possible values for '-relief' are: ## flat, groove, raised, ridge, solid, and sunken. ##+####################################################################### ## For BUTTON widgets: set PADYpx_button 0 set PADXpx_button 0 set BDwidthPx_button 2 set RELIEF_button raised ## For LABEL widgets: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 set RELIEF_label flat ## For TEXT widgets: set BDwidthPx_text 2 # set RELIEF_text raised set RELIEF_text ridge ## For RADIOBUTTON widgets: set BDwidthPx_radbutt 2 # set RELIEF_radbutt raised set RELIEF_radbutt ridge ## For ENTRY widgets: set BDwidthPx_entry 2 set RELIEF_entry sunken ## For LISTBOX widgets: set BDwidthPx_listbox 2 # set RELIEF_listbox raised set RELIEF_listbox sunken set initListboxWidthChars 20 set initListboxHeightChars 12 ## SCALE geom parameters: (not used) # set BDwidthPx_scale 2 # set RELIEF_scale sunken # set scaleLengthPx 300 # set scaleThicknessPx 10 ## CANVAS geom parms: (not used) # # set BDwidthPx_canvas 2 # set BDwidthPx_canvas 0 # set RELIEF_canvas raised # set initCanWidthPx 200 # set initCanHeightPx 200 # set minCanHeightPx 24 ##+######################################################################## ## 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" set aRtext(buttonCALC2from1) "Calc 2from1" set aRtext(buttonCLEAR2) "Clear2" set aRtext(buttonCALC1from2) "Calc 1from2" set aRtext(buttonCLEAR1) "Clear1" ## For '.fRleft.fRunits1' and '.fRleft.fRunits2' frame: set aRtext(labelUNITS1) " Units1:" set aRtext(labelUNITS2) " Units2:" ## For '.fRleft.fRvalues1' and '.fRleft.fRvalues2' frame: set aRtext(labelCOEF) "Coefficient:" set aRtext(labelEXP) "Exponent (base 10):" set aRtext(buttonMINUS) "-" set aRtext(buttonPLUS) "+" ## END OF if { "$VARlocale" == "en" ##+################################################################### ## Set a MINSIZE of the window (roughly). (OR fix the window size.) ## ## For WIDTH, allow for a minwidth of the '.fRbuttons' frame: ## several buttons (UseIt,Cancel,Help,Calc2from1,Calc1from2). ## ## For HEIGHT, allow ## 1 chars high for the '.fRbuttons' frame ## 4 chars high for the '.fRmsg' frame ## 1 char high for the '.fRleft.fRunits1' frame ## 1 char high for the '.fRleft.fRvalues1' frame ## 1 char high for the '.fRleft.fRunits2' frame ## 1 char high for the '.fRleft.fRvalues2' frame ##+####################################################################### ## If we allow the window to be resizable, we could pack the listbox with ## '-fill both -expand 1' so that the listbox can be enlarged by ## enlarging the window. ##+####################################################################### set minWinWidthPx [font measure fontTEMP_varwidth \ "$aRtext(buttonUSEIT) $aRtext(buttonCANCEL) $aRtext(buttonHELP) \ $aRtext(buttonCALC2from1) $aRtext(buttonCALC1from2)"] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 5 widgets x 4 pixels/widget for borders/padding ## for buttons and labels. set minWinWidthPx [expr {28 + $minWinWidthPx}] ##+############################################################# ## Set MIN HEIGHT: ## 1 char high for the '.fRbuttons' frame ## 4 chars high for the '.fRmsg' frame ## 1 char high for the '.fRleft.fRunits1' frame ## 1 char high for the '.fRleft.fRvalues1' frame ## 1 char high for the '.fRleft.fRunits2' frame ## 1 char high for the '.fRleft.fRvalues2' frame ##+############################################################# set CharHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {9 * $CharHeightPx}] ## Add about 28 pixels for top-bottom window decoration, ## about 6 frames x 4 pixels/frame for each of the stacked frames/widgets ## --- their borders/padding. set minWinHeightPx [expr {52 + $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 ##+#################################################################### ##+#################################################################### ## DEFINE *ALL* THE FRAMES: ## TOP-LEVEL FRAMES: ## '.fRbuttons' - to contain several buttons (UseIt,Cancel,Help,Calc,Calc). ## '.fRmsg' - for 1 label widget ## '.fRleft' - for 4 lines of widgets ## '.fRright' - for a listbox widget (with x,y scrollbars) ## ## SUB-FRAMES: ## '.fRleft.fRunits1' - to contain 1 label, text, and radiobutton widget. ## '.fRleft.fRvalues1' - to contain 2 entry widgets, with labels. ## '.fRleft.fRunits2' - to contain 1 label, text, and radiobutton widget. ## '.fRleft.fRvalues2' - to contain 2 entry widgets, with labels. ##+#################################################################### ##+#################################################################### ## FOR TESTING change 0 to 1: ## (Example1: To see appearance of frames when borders are drawn.) ## (Example2: To see sizes of frames for various '-fill' options.) ## (Example3: To see how frames expand as window is resized.) if {0} { set RELIEF_frame raised set BDwidthPx_frame 2 } else { set RELIEF_frame flat set BDwidthPx_frame 0 } frame .fRbuttons -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRmsg -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRleft -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRright -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRleft.fRunits1 -relief $RELIEF_frame -borderwidth $BDwidthPx_frame # frame .fRleft.fRunits1 -relief raised -borderwidth 2 # frame .fRleft.fRvalues1 -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRleft.fRvalues1 -relief raised -borderwidth 2 frame .fRleft.fRunits2 -relief $RELIEF_frame -borderwidth $BDwidthPx_frame # frame .fRleft.fRunits2 -relief raised -borderwidth 2 # frame .fRleft.fRvalues2 -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRleft.fRvalues2 -relief raised -borderwidth 2 ##+######################################################## ## PACK the TOP-LEVEL FRAMES. ##+######################################################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRmsg \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRleft \ -side left \ -anchor nw \ -fill x \ -expand 1 pack .fRright \ -side left \ -anchor nw \ -fill y \ -expand 1 ##+########################################## ## PACK the 2nd-LEVEL FRAMES. ##+########################################## pack .fRleft.fRunits1 \ .fRleft.fRvalues1 \ -side top \ -anchor nw \ -fill x \ -expand 1 pack .fRleft.fRunits2 \ .fRleft.fRvalues2 \ -side top \ -anchor nw \ -fill x \ -expand 1 ##+################################################################ ## The frames are defined and packed. ##+################################################################ ## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES. ##+################################################################ ##+################################################################ ##+######################################################## ## IN THE '.fRbuttons' frame -- DEFINE several BUTTONS --- ## 1 'UseIt' BUTTON, 1 'Cancel' BUTTON, and 1 'Help' BUTTON. ## Also define a pair of LABEL and TEXT widgets. ## THEN PACK THE WIDGETS. ##+######################################################## button .fRbuttons.buttUSEIT \ -text "$aRtext(buttonUSEIT)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {put_vars} button .fRbuttons.buttCANCEL \ -text "$aRtext(buttonCANCEL)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {exit} button .fRbuttons.buttHELP \ -text "$aRtext(buttonHELP)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {popup_msgVarWithScroll .topHelp "$HELPtext" +10+10} button .fRbuttons.buttCALC2from1 \ -text "$aRtext(buttonCALC2from1)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {area_update "2from1"} button .fRbuttons.buttCLEAR2 \ -text "$aRtext(buttonCLEAR2)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {clear_values2} button .fRbuttons.buttCALC1from2 \ -text "$aRtext(buttonCALC1from2)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {area_update "1from2"} button .fRbuttons.buttCLEAR1 \ -text "$aRtext(buttonCLEAR1)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {clear_values1} ## Pack the widgets in frame '.fRbuttons'. pack .fRbuttons.buttUSEIT \ .fRbuttons.buttCANCEL \ .fRbuttons.buttHELP \ .fRbuttons.buttCALC2from1 \ .fRbuttons.buttCLEAR2 \ .fRbuttons.buttCALC1from2 \ .fRbuttons.buttCLEAR1 \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE '.fRmsg' frame -- ## DEFINE 1 LABEL WIDGET. THEN PACK IT. ##+######################################################## label .fRmsg.labelMSG \ -text "" \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief raised \ -bg $msgBKGD pack .fRmsg.labelMSG \ -side left \ -anchor w \ -fill x \ -expand 1 ##+######################################################## ## Set character-widths of label, text, and entry widgets ## in the 'fRleft' frame --- to be used below. ## This allows for easy and consistent changes. ##+######################################################## set LabelWidthChars_forUnits 6 set TextWidthChars_forUnits 17 set EntryWidthChars_forCoef 16 set EntryWidthChars_forExp 4 ##+######################################################## ## IN THE '.fRleft.fRunits1' frame -- ## DEFINE 1 pair of LABEL and TEXT widgets and 1 RADIOBUTTON. ## THEN PACK THEM. ##+######################################################## label .fRleft.fRunits1.labelUNITS1 \ -text "$aRtext(labelUNITS1)" \ -font fontTEMP_label \ -width $LabelWidthChars_forUnits \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits1.txtUNITS1 \ -height 1 \ -width $TextWidthChars_forUnits \ -font fontTEMP_text \ -wrap none \ -borderwidth $BDwidthPx_text \ -relief $RELIEF_text \ -bg $textBKGD radiobutton .fRleft.fRunits1.radbuttUNITS1 \ -variable RADVARunits1or2 \ -value "units1" \ -text "units1" \ -font fontTEMP_button \ -anchor w \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt \ -selectcolor $radbuttSELECTCOLOR \ -bg $radbuttBKGD ## PACK the widgets in frame '.fRleft.fRunits1'. pack .fRleft.fRunits1.labelUNITS1 \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRunits1.txtUNITS1 \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRunits1.radbuttUNITS1 \ -side right \ -anchor e \ -fill none \ -expand 0 ##+######################################################## ## IN THE '.fRleft.fRvalues1' frame -- ## DEFINE 2 pairs of LABEL and ENTRY widgets. THEN PACK THEM. ##+######################################################## label .fRleft.fRvalues1.labelCOEF \ -text "$aRtext(labelCOEF)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label entry .fRleft.fRvalues1.entryCOEF1 \ -textvariable ENTRYcoef1 \ -width $EntryWidthChars_forCoef \ -font fontTEMP_entry \ -relief sunken \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry \ -bg $entryBKGD label .fRleft.fRvalues1.labelEXP \ -text "$aRtext(labelEXP)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label button .fRleft.fRvalues1.buttEXPminus \ -text "$aRtext(buttonMINUS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {adjust_values1 -1} entry .fRleft.fRvalues1.entryEXP1 \ -textvariable ENTRYexp1 \ -width $EntryWidthChars_forExp \ -font fontTEMP_entry \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry \ -bg $entryBKGD button .fRleft.fRvalues1.buttEXPplus \ -text "$aRtext(buttonPLUS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {adjust_values1 +1} ## Pack all widgets in frame '.fRleft.fRvalues1'. pack .fRleft.fRvalues1.labelCOEF \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRvalues1.entryCOEF1 \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRleft.fRvalues1.labelEXP \ .fRleft.fRvalues1.buttEXPminus \ .fRleft.fRvalues1.entryEXP1 \ .fRleft.fRvalues1.buttEXPplus \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE '.fRleft.fRunits2' frame -- ## DEFINE 1 pair of LABEL and TEXT widgets and 1 RADIOBUTTON. ## THEN PACK THEM. ##+######################################################## label .fRleft.fRunits2.labelUNITS2 \ -text "$aRtext(labelUNITS2)" \ -width $LabelWidthChars_forUnits \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits2.txtUNITS2 \ -height 1 \ -width $TextWidthChars_forUnits \ -font fontTEMP_text \ -wrap none \ -borderwidth $BDwidthPx_text \ -relief $RELIEF_text \ -bg $textBKGD radiobutton .fRleft.fRunits2.radbuttUNITS2 \ -variable RADVARunits1or2 \ -value "units2" \ -text "units2" \ -font fontTEMP_button \ -anchor w \ -bd $BDwidthPx_radbutt \ -relief $RELIEF_radbutt \ -selectcolor $radbuttSELECTCOLOR \ -bg $radbuttBKGD ## PACK the widgets in frame '.fRleft.fRunits2'. pack .fRleft.fRunits2.labelUNITS2 \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRunits2.txtUNITS2 \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRunits2.radbuttUNITS2 \ -side right \ -anchor e \ -fill none \ -expand 0 ##+######################################################## ## IN THE '.fRleft.fRvalues2' frame -- ## DEFINE 2 pairs of LABEL and ENTRY widgets. THEN PACK THEM. ##+######################################################## label .fRleft.fRvalues2.labelCOEF \ -text "$aRtext(labelCOEF)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label entry .fRleft.fRvalues2.entryCOEF2 \ -textvariable ENTRYcoef2 \ -width $EntryWidthChars_forCoef \ -font fontTEMP_entry \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry \ -bg $entryBKGD label .fRleft.fRvalues2.labelEXP \ -text "$aRtext(labelEXP)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label button .fRleft.fRvalues2.buttEXPminus \ -text "$aRtext(buttonMINUS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {adjust_values2 -1} entry .fRleft.fRvalues2.entryEXP2 \ -textvariable ENTRYexp2 \ -width $EntryWidthChars_forExp \ -font fontTEMP_entry \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry \ -bg $entryBKGD button .fRleft.fRvalues2.buttEXPplus \ -text "$aRtext(buttonPLUS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {adjust_values2 +1} ## Pack all widgets in frame '.fRleft.fRvalues2'. pack .fRleft.fRvalues2.labelCOEF \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRvalues2.entryCOEF2 \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRleft.fRvalues2.labelEXP \ .fRleft.fRvalues2.buttEXPminus \ .fRleft.fRvalues2.entryEXP2 \ .fRleft.fRvalues2.buttEXPplus \ -side left \ -anchor w \ -fill none \ -expand 0 ##+###################################################### ## In the '.fRright' frame - ## DEFINE-and-PACK 1 LISTBOX widget. ##+###################################################### listbox .fRright.listbox \ -width $initListboxWidthChars \ -height $initListboxHeightChars \ -font fontTEMP_listbox \ -borderwidth $BDwidthPx_listbox \ -relief $RELIEF_listbox \ -state normal \ -yscrollcommand ".fRright.scrbary set" \ -xscrollcommand ".fRright.scrbarx set" scrollbar .fRright.scrbary \ -orient vertical -command ".fRright.listbox yview" scrollbar .fRright.scrbarx \ -orient horizontal -command ".fRright.listbox xview" ## Pack the widgets in frame '.fRright'. pack .fRright.scrbary \ -side right \ -anchor e \ -fill y \ -expand 0 pack .fRright.scrbarx \ -side bottom \ -anchor sw \ -fill x \ -expand 0 pack .fRright.listbox \ -side top \ -anchor nw \ -fill y \ -expand 1 ##+####################################### ## END OF MAIN SECTION TO SETUP THE GUI. ## FRAMES AND WIDGETS ARE DEFINED. ##+####################################### ##+##################################################################### ##+##################################################################### ## BINDINGS section: ## - button-release-1 binding on the listbox widget ## - key-press bindings on the 4 entry widgets. ##+##################################################################### ##+##################################################################### bind .fRright.listbox "listbox_select_units" # set DATAchangeEVENT "ButtonPress-1" set DATAchangeEVENT "KeyPress" bind .fRleft.fRvalues1.entryCOEF1 <$DATAchangeEVENT> { ## If it looks like the user is changing coef1, then blank values2 ## --- since the 2 areas may no longer be equivalent. set ENTRYcoef2 "" set ENTRYexp2 "0" } bind .fRleft.fRvalues1.entryEXP1 <$DATAchangeEVENT> { ## If it looks like the user is changing exp1, then blank values2 ## --- since the 2 areas may no longer be equivalent. set ENTRYcoef2 "" set ENTRYexp2 "0" } bind .fRleft.fRvalues2.entryCOEF2 <$DATAchangeEVENT> { ## If it looks like the user is changing coef2, then blank values1 ## --- since the 2 areas may no longer be equivalent. set ENTRYcoef1 "" set ENTRYexp1 "0" } bind .fRleft.fRvalues2.entryEXP2 <$DATAchangeEVENT> { ## If it looks like the user is changing exp2, then blank values1 ## --- since the 2 areas may no longer be equivalent. set ENTRYcoef1 "" set ENTRYexp1 "0" } ##+##################################################################### ##+##################################################################### ## DEFINE PROCEDURES: ## ## 'listbox_select_units' - called by a button1-release binding on ## the listbox widget. ## ## 'area_update' - called by the 'Calc' buttons ## ## 'adjust_values1' - called by the '+' and '-' buttons for exp1 ## 'adjust_values2' - called by the '+' and '-' buttons for exp2 ## ## 'clear_values2' - called by button 'Clear2' ## 'clear_values1' - called by button 'Clear1' ## ## 'edit_inputs' - called by the 'area_update' proc ## 'decimal_check' - called by the 'edit_inputs' proc ## ## 'normalize_coef_exp' - called by the 'area_update' proc ## (not implemented yet) ## ## 'put_vars' - called by the 'UseIt' button ## ## 'advise_user' - called at bottom of script in the 'Additional ## GUI Initialization' section --- and could be ## called in one or more of the procs above. ## ## 'popup_msgVarWithScroll' - called by the 'Help' button and by the ## 'edit_inputs' proc. ##+##################################################################### ##+##################################################################### ##+################################################################ ## PROC 'listbox_select_units' ##+################################################################ ## PURPOSE: Sets some 'units' variables depending on the ## current setting of the radiobutton variable RADVARunits1or2. ## ## CALLED BY: binding on the listbox widget ##+################################################################ proc listbox_select_units {} { global RADVARunits1or2 CHOSEunits1 CHOSEunits2 global ENTRYcoef1 ENTRYexp1 ENTRYcoef2 ENTRYexp2 set sel_index [ .fRright.listbox curselection ] if { $sel_index != "" } { set selectedUNITS [ .fRright.listbox get $sel_index ] } ## FOR TESTING: # puts "listbox_select_units: selectedUNITS = $selectedUNITS" ######################################################## ## Load in the units name in a CHOSEunits variable and in ## a couple of text widgets. ######################################################## if {"$RADVARunits1or2" == "units1"} { set CHOSEunits1 "$selectedUNITS" .fRleft.fRunits1.txtUNITS1 delete 1.0 end .fRleft.fRunits1.txtUNITS1 insert end "$selectedUNITS" ## User is changing units1 and may want to use coef2. ## If coef2 is nonblank, blank coef1 and set exp1 to zero --- ## to reduce instances of the 2 areas not showing as equivalent. if {"$ENTRYcoef2" != ""} { set ENTRYcoef1 "" set ENTRYexp1 "0" } } if {"$RADVARunits1or2" == "units2"} { set CHOSEunits2 "$selectedUNITS" .fRleft.fRunits2.txtUNITS2 delete 1.0 end .fRleft.fRunits2.txtUNITS2 insert end "$selectedUNITS" ## User is changing units2 and may want to use coef1. ## If coef1 is nonblank, blank coef2 and set exp2 to zero --- ## to reduce instances of the 2 areas not showing as equivalent. if {"$ENTRYcoef1" != ""} { set ENTRYcoef2 "" set ENTRYexp2 "0" } } } ## END OF PROC 'listbox_select_units' ##+##################################################################### ## PROC: 'area_update' ##+##################################################################### ## ## PURPOSE: If the argument to this proc is the string "2from1", ## use CHOSEunits1 ENTRYcoef1 ENTRYexp1 and CHOSEunits2 ## to compute ENTRYcoef2 and ENTRYexp2. ## AND ## If the argument to this proc is the string "1from2", ## use CHOSEunits2 ENTRYcoef2 ENTRYexp2 and CHOSEunits1 ## to compute ENTRYcoef1 and ENTRYexp1. ## ## The VARcoef that is computed could be normalized to be ## between 1.0 and 9.999... ## ## The VARexp that is computed can be any integer --- ## negative, zero, or positive. ## ## CALLED BY: the 'Calc 2from1' or 'Calc 1from2' buttons. ##+##################################################################### proc area_update {CONVERTtype} { ## Dummy out this proc, for testing. # return ######################################################################## ## The variable 'CONVERTtype' should be the string "2from1" or "1from2". ######################################################################## global CHOSEunits1 ENTRYcoef1 ENTRYexp1 \ CHOSEunits2 ENTRYcoef2 ENTRYexp2 \ aRcoef aRexp EDITcode FORMATforCOEF ######################################################################## ## Check the coefficient and exponent inputs for errors. ## Bail out if there seems to be invalid input. ######################################################################## edit_inputs $CONVERTtype if {$EDITcode > 0} {return} #################################################### ## Set factors to be used in the conversion --- ## both '2from1' and '1from2'. #################################################### eval set CoefUnits1PerStd \$aRcoef($CHOSEunits1/square-meter) eval set ExpUnits1PerStd \$aRexp($CHOSEunits1/square-meter) eval set CoefUnits2PerStd \$aRcoef($CHOSEunits2/square-meter) eval set ExpUnits2PerStd \$aRexp($CHOSEunits2/square-meter) ################################################################### ## If CONVERTtype = '2from1', use CHOSEunits1 ENTRYcoef1 ENTRYexp1 ## and CHOSEunits2 to calculate ENTRYcoef2 and ENTRYexp2. ## ## Note that ENTRYcoef1 is area in units1. ## To convert ENTRYcoef1 to units2 in ENTRYcoef2, ## 1) we can convert ENTRYcoef1 to 'standard' units (square-meters) by ## dividing by CoefUnits1PerStd, ## 2) we convert 'standard' units (square-meters) to units2 by ## multiplying by CoefUnits2PerStd. ## ## To convert the exponent ENTRYexp1 to units2 in ENTRYexp2, ## note that dividing by 10 to an exponent is accomplished by ## subracting exponents --- and multiplication by 10 to an exponent ## is accomplished by adding exponents. So ## 1) we can convert ENTRYexp1 to 'standard' units (square-meters) ## by subtracting ExpUnits1PerStd, ## 2) we convert 'standard' units (square-meters) to units2 ## by adding ExpUnits2PerStd. ################################################################### if {"$CONVERTtype" == "2from1"} { set ENTRYcoef2 [expr {($ENTRYcoef1/$CoefUnits1PerStd)*$CoefUnits2PerStd}] set ENTRYexp2 [expr {($ENTRYexp1 - $ExpUnits1PerStd) + $ExpUnits2PerStd}] # normalize_coef_exp ENTRYcoef2 ENTRYexp2 set ENTRYcoef2 [ format "$FORMATforCOEF" $ENTRYcoef2] } ################################################################### ## If CONVERTtype = '1from2', use CHOSEunits2 ENTRYcoef2 ENTRYexp2 ## and CHOSEunits1 to calculate ENTRYcoef1 and ENTRYexp1. ## ## Note that ENTRYcoef2 is area in units2. ## To convert ENTRYcoef2 to units1 in ENTRYcoef1, ## 1) we can convert ENTRYcoef2 to 'standard' units (square-meters) by ## dividing by CoefUnits2PerStd, ## 2) we convert 'standard' units (square-meters) to units1 by ## multiplying by CoefUnits1PerStd. ## ## To convert the exponent ENTRYexp2 to units1 in ENTRYexp1, ## note that dividing by 10 to an exponent is accomplished by ## subracting exponents --- and multiplication by 10 to an exponent ## is accomplished by adding exponents. So ## 1) we can convert ENTRYexp2 to 'standard' units (square-meters) ## by subtracting ExpUnits2PerStd, ## 2) we convert 'standard' units (square-meters) to units1 ## by adding ExpUnits1PerStd. ################################################################### if {"$CONVERTtype" == "1from2"} { set ENTRYcoef1 [expr {($ENTRYcoef2/$CoefUnits2PerStd)*$CoefUnits1PerStd}] set ENTRYexp1 [expr {($ENTRYexp2 - $ExpUnits2PerStd) + $ExpUnits1PerStd}] # normalize_coef_exp ENTRYcoef1 ENTRYexp1 set ENTRYcoef1 [ format "$FORMATforCOEF" $ENTRYcoef1] } } ## END of proc 'area_update' ##+##################################################################### ## PROC: 'adjust_values1' ##+##################################################################### ## ## PURPOSE: Uses the plus or minus 1 argument to this proc to ## increment/decrement ENTRYexp1 and adjust ENTRYcoef1 ## accordingly by a factor of 10. ## ## CALLED BY: the '+' or '-' buttons for exp1. ##+##################################################################### proc adjust_values1 {plusORminus1} { ## Dummy out this proc, for testing. # return ######################################################################## ## The variable 'plusORminus1' should be -1 or 1. ######################################################################## global ENTRYcoef1 ENTRYexp1 FORMATforCOEF if {"$ENTRYcoef1" == "" || "$ENTRYexp1" == ""} { return } set ENTRYexp1 [expr {$ENTRYexp1 + $plusORminus1}] set ENTRYcoef1 [expr {$ENTRYcoef1 * pow(10,-$plusORminus1)}] set ENTRYcoef1 [ format "$FORMATforCOEF" $ENTRYcoef1] } ## END OF PROC 'adjust_values1' ##+##################################################################### ## PROC: 'adjust_values2' ##+##################################################################### ## ## PURPOSE: Uses the plus or minus 1 argument to this proc to ## increment/decrement ENTRYexp2 and adjust ENTRYcoef2 ## accordingly by a factor of 10. ## ## CALLED BY: the '+' or '-' buttons for exp2. ##+##################################################################### proc adjust_values2 {plusORminus1} { ## Dummy out this proc, for testing. # return ######################################################################## ## The variable 'plusORminus1' should be -1 or 1. ######################################################################## global ENTRYcoef2 ENTRYexp2 FORMATforCOEF if {"$ENTRYcoef2" == "" || "$ENTRYexp2" == ""} { return } set ENTRYexp2 [expr {$ENTRYexp2 + $plusORminus1}] set ENTRYcoef2 [expr {$ENTRYcoef2 * pow(10,-$plusORminus1)}] set ENTRYcoef2 [ format "$FORMATforCOEF" $ENTRYcoef2] } ## END OF PROC 'adjust_values2' ##+##################################################################### ## PROC: 'clear_values2' ##+##################################################################### ## PURPOSE: Clears variables ENTRYcoef2 and ENTRYexp2. ## ## CALLED BY: button 'Clear2' ##+##################################################################### proc clear_values2 {} { global ENTRYcoef2 ENTRYexp2 set ENTRYcoef2 "" # set ENTRYexp2 "" set ENTRYexp2 "0" } ## END OF PROC 'clear_values2' ##+##################################################################### ## PROC: 'clear_values1' ##+##################################################################### ## PURPOSE: Clears variables ENTRYcoef1 and ENTRYexp1. ## ## CALLED BY: button 'Clear1' ##+##################################################################### proc clear_values1 {} { global ENTRYcoef1 ENTRYexp1 set ENTRYcoef1 "" # set ENTRYexp1 "" set ENTRYexp1 "0" } ## END OF PROC 'clear_values1' ##+#################################################################### ## PROC: 'edit_inputs' ##+##################################################################### ## PURPOSE: If the argument 'CONVERTtype' to this proc is the string "2from1", ## we will use ## CHOSEunits1 ENTRYcoef1 ENTRYexp1 and CHOSEunits2 ## to compute ENTRYcoef2 and ENTRYexp2 --- ## so check ENTRYcoef1 and ENTRYexp1. ## AND ## If the argument 'CONVERTtype' to this proc is the string "1from2", ## we will use ## CHOSEunits2 ENTRYcoef2 ENTRYexp2 and CHOSEunits1 ## to compute ENTRYcoef1 and ENTRYexp1 --- ## so check ENTRYcoef2 and ENTRYexp2. ## ## Pops up an error message if the data is invalid. ## ## CALLED BY: the 'area_update' proc ##+##################################################################### proc edit_inputs {CONVERTtype} { # global CHOSEunits1 CHOSEunits2 global ENTRYcoef1 ENTRYexp1 ENTRYcoef2 ENTRYexp2 ## We could do without this EDITcode variable, by using ## a code with the 'return' statement. But using this ## code variable is a little more self-documenting. global EDITcode set EDITcode 0 ########################################################### ## Set some message strings. (Could do this once globally.) ########################################################### set MSGblank "is blank. Must NOT be blank." set MSGnotInteger " is NOT INTEGER." set NUMERICmsg "should be a decimal number. Examples: 1.234 or 0.56 or -.789" ####################################################### ## Do the checks for CONVERTtype '2from1'. ####################################################### if {"$CONVERTtype" == "2from1"} { ####################################################### ## Remove trailing and leading blanks (if any) from the ## user entries in the 'entry2' widgets. ####################################################### set ENTRYcoef1 [string trim $ENTRYcoef1] set ENTRYexp1 [string trim $ENTRYexp1] ######################################################################### ## Check that ENTRYcoef1 ENTRYexp1 are NOT blank. ######################################################################### if {"$ENTRYcoef1" == ""} { popup_msgVarWithScroll .topErr "Coefficient for UNITS1 area $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYexp1" == ""} { popup_msgVarWithScroll .topErr "Exponent for UNITS1 area $MSGblank" +10+10 set EDITcode 1 return } ########################################################## ## Check that ENTRYexp1 is integer. ########################################################## if {![string is integer -strict "$ENTRYexp1"]} { popup_msgVarWithScroll .topErr "Exponent for area in UNITS1 $MSGnotInteger" +10+10 set EDITcode 1 return } ######################################################################### ## Check that ENTRYcoef1 is a decimal number --- such as 1.357. ######################################################################### if {![decimal_check "$ENTRYcoef1"]} { popup_msgVarWithScroll .topErr "Coefficient for UNITS1 area $NUMERICmsg" +10+10 set EDITcode 1 return } } ## END OF if {"$CONVERTtype" == "2from1"} ####################################################### ## Do the checks for CONVERTtype '1from2'. ####################################################### if {"$CONVERTtype" == "1from2"} { ####################################################### ## Remove trailing and leading blanks (if any) from the ## user entries in the 'entry1' widgets. ####################################################### set ENTRYcoef2 [string trim $ENTRYcoef2] set ENTRYexp2 [string trim $ENTRYexp2] ######################################################################### ## Check that ENTRYcoef2 ENTRYexp2 are NOT blank. ######################################################################### if {"$ENTRYcoef2" == ""} { popup_msgVarWithScroll .topErr "Coefficient for UNITS2 area $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYexp2" == ""} { popup_msgVarWithScroll .topErr "Exponent for UNITS2 area $MSGblank" +10+10 set EDITcode 1 return } ########################################################## ## Check that ENTRYexp2 is integer. ########################################################## if {![string is integer -strict "$ENTRYexp2"]} { popup_msgVarWithScroll .topErr "Exponent for area in UNITS2 $MSGnotInteger" +10+10 set EDITcode 1 return } ######################################################################### ## Check ENTRYcoef2 is a decimal number --- such as 1.357. ######################################################################### if {![decimal_check "$ENTRYcoef2"]} { popup_msgVarWithScroll .topErr "Coefficient for UNITS2 area $NUMERICmsg" +10+10 set EDITcode 1 return } } ## END OF if {"$CONVERTtype" == "1from2"} } ## END of proc 'edit_inputs' ##+######################################################################## ## PROC 'decimal_check' ##+######################################################################## ## PURPOSE: Returns 1 or 0 if the input string looks like a decimal number ## --- positive or negative. Example numbers that are OK: ## 1.234 12.34 0.234 .234 6 ## -1.234 -12.34 -0.234 -.234 -6 ########################################################################### ## References (lots of one-liners): ## http://stackoverflow.com/questions/2072222/regular-expression-for-positive-and-a-negative-decimal-value-in-java ## http://stackoverflow.com/questions/308122/simple-regular-expression-for-a-decimal-with-a-precision-of-2 ## http://stackoverflow.com/questions/4246077/matching-numbers-with-regular-expressions-only-digits-and-commas/4247184#4247184 ## ## More specific to Tcl-Tk (including multi-liners): ## http://wiki.tcl.tk/989 'Regular Expression Examples' ## http://wiki.tcl.tk/768 'Entry Validation' - See "Integer forbidding leading zero:" ## http://wiki.tcl.tk/10166 'string is' ## http://wiki.tcl.tk/40710 'significant digits rounding' - uses regexp to split a number - ## Splits using: if {[regexp {^([+,-]?)([0-9]+)(\.?[0-9]*)?([eE][+-]?[0-9]+)?$} $num -> s i d e]} ## Removes leading zero with: regexp {^(0*)([1-9][0-9]*)$} $i -> NULL DIG ## http://wiki.tcl.tk/530 'Unit converter' has a regexp to parse numbers: ## set RE {(?ix) # Ignore case, extended syntax ## ([-+]?) # Optional leading sign ## ([0-9]*) # Integer part ## \.? # Optional decimal point ## ([0-9]*) # Fractional part ## (e?[0-9]*) # Optional exponent ## } ## ########################################################################### ## I do not mind incurring a little (minute amount of) processing ## with a multiple-line implementation. Probably easier to fix if ## a string gets through --- such as ".0.3" (two decimal points). ## ## CALLED BY: proc 'edit_inputs' ##+######################################################################## proc decimal_check {string} { set PosDecimalOK [regexp {^([0-9]*)\.?([0-9]*)$} "$string"] set NegDecimalOK [regexp {^\-([0-9]*)\.?([0-9]*)$} "$string"] set PosNakedDecimalOK [regexp {^\.?([0-9]*)$} "$string"] set NegNakedDecimalOK [regexp {^\-\.?([0-9]*)$} "$string"] set IntegerOK [string is integer $string] set retCODE [expr { \ $PosDecimalOK || $NegDecimalOK || \ $PosNakedDecimalOK || $NegNakedDecimalOK || \ $IntegerOK }] ## FOR TESTING: if {0} { puts "" puts "decimal_check:" puts "string: $string" puts "PosDecimalOK: $PosDecimalOK" puts "NegDecimalOK: $NegDecimalOK" puts "PosNakedDecimalOK: $PosNakedDecimalOK" puts "NegNakedDecimalOK: $NegNakedDecimalOK" puts "IntegerOK: $IntegerOK" puts "retCODE: $retCODE" } return $retCODE } ## END of proc 'decimal_check' ##+##################################################################### ## PROC: 'put_vars' ## ## PURPOSED: Put contents of 6 variables --- ## CHOSEunits1 ENTRYcoef1 ENTRYexp1 CHOSEunits2 ENTRYcoef2 ENTRYexp2 --- ## to standard output. ## ## CALLED BY: button .fRbuttons.buttUSEIT ##+##################################################################### proc put_vars { } { global CHOSEunits1 ENTRYcoef1 ENTRYexp1 CHOSEunits2 ENTRYcoef2 ENTRYexp2 EDITcode ## Check that the outputs are valid --- esp. non-blank. edit_inputs 2from1 if {$EDITcode > 0} {return} edit_inputs 1from2 if {$EDITcode > 0} {return} ## Put the units, coefficients, and exponents to stdout. puts "$CHOSEunits1 $ENTRYcoef1 $ENTRYexp1 $CHOSEunits2 $ENTRYcoef2 $ENTRYexp2" exit } ## END of proc 'puts_vars' ##+##################################################################### ## PROC 'advise_user' ##+##################################################################### ## PURPOSE: Puts a message to the user on the GUI. ## ## CALLED BY: the 'Additional GUI Initialization' section at ## the bottom of this script. ## May also be called by some procs such as ## 'area_update'. ##+##################################################################### proc advise_user {text} { .fRmsg.labelMSG configure -text "$text" ## Make sure the text is displayed on the GUI. update ## Alternatively, we could put the message in the title-bar ## of the GUI window. (But it is easy for the user to ## fail to see the message there. Besides, we have more ## options in displaying the message by putting it on a ## Tk widget in the GUI.) ## # wm title . "$text" } ## END OF PROC 'advise_user' ##+######################################################################## ## 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 ULloc} { ## global fontTEMP_varwidth #; Not needed. 'wish' makes this global. ## global env # 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 geometry $toplevName $ULloc 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_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" ## -font fontTEMP_varwidth 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_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 ## -font fontTEMP_varwidth } button $toplevName.butt \ -text "OK" \ -font fontTEMP_varwidth \ -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 } ################################################ ## Set some 'event' bindings to allow for ## easy scrolling through huge listings. ## is a press of the Page-Down key. ## is a press of the Page-Up key. ## is a press of the Home key ## to go to the top of the listing. ## is a press of the End key ## to go to the bottom of the listing. ## is a press of the Up-arrow key. ## is a press of the Down-arrow key. ################################################ bind $toplevName "$toplevName.text yview scroll +1 page" bind $toplevName "$toplevName.text yview scroll -1 page" bind $toplevName "$toplevName.text see 1.0" bind $toplevName "$toplevName.text see end" bind $toplevName "$toplevName.text yview scroll -1 unit" bind $toplevName "$toplevName.text yview scroll +1 unit" ##################################### ## 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. ##+############################################### ## SET HELP TEXT variable. ##+############################################### set HELPtext \ " ************ tkArea - Converter / Selector Utility ********* This Tk GUI script provides a GUI for CONVERTING an area in one unit of measure to the area in a second unit of measure. The area in either 'Units1' and 'Units2' is specified in 'scientific notation' --- that is, as a coefficient and an exponent (base 10) --- with the coefficient being a decimal number. When entered as input, the coefficient is typically 'normalized' between 1.0 and 10.0 or between 1.0 and 1000.0, say. The exponents can be positive integers or zero or negative integers --- in other words, the exponent entry should be an integer. The '+' and '-' buttons on either side of the two exponent entry fields allow for adjusting the coefficient and exponent to a pair of values that are more meaningful to the user. ******************* METHOD OF OPERATION: ******************* STEP 1: (setting units-of-measure) The user sets Units1 and Units2 via the listbox and the two 'units1' and 'units2' radiobutton widgets on the GUI. Click on a radiobutton and then click on a unit-of-measure in the listbox. STEP 2: (calculating one area from another) There are 4 'entry' widgets on the GUI: - TWO for Units1 coefficient and exponent and - TWO for Units2 coefficient and exponent --- Enter 2 values in the Units1 coefficient and exponent entry fields and click on the 'Calc 2from1' button. OR Enter 2 values in the Units2 coefficient and exponent entry fields and click on the 'Calc 1from2' button. ******************************* SELECTION AS WELL AS CONVERSION: ******************************* In addition to CONVERTING areas, this GUI is also for SELECTING the area --- expressed as both the Units1 coefficient and exponent AND the Units2 coefficient and exponent. When the user clicks on the 'UseIt' button, the area is written as a text string to 'stdout' in both forms. Sample output: square-meters 1.0 0 square-yards 1.1960 0 Thus this utility Tk script can be called from within a shell script or from within a Tcl-Tk script to get an area. *********************************************** EXAMPLES OF CALLING THIS SCRIPT WITHIN A SCRIPT: *********************************************** This Tk script can be called within a shell or Tcl-Tk script in a form such as the following: TEMP=`\$SCRIPT_LOC/tkAreaConvertSelect.tk` OR in the form TEMP=`\$SCRIPT_LOC/tkAreaConvertSelect.tk coef1 exp1 units1 units2` with specific values such as TEMP=`\$SCRIPT_LOC/tkAreaConvertSelect.tk 6.0 1 square-inches square-centimeters` The coef2 and exp2 values can then be computed from coef1, exp1, units1 and units2. Note: The values in TEMP can be extracted with a command like 'cut' or 'awk' in a shell script --- and 'string' commands in a Tcl-Tk script. Hence ... IN A SHELL SCRIPT OR ANOTHER TCL-TK SCRIPT, this Tcl-Tk script can ACT AS AN AREA CONVERTER-SELECTOR by calculating and outputting a currently specified area to 'stdout' (standard output), when the 'UseIt' button is clicked. Example output string format: units1 coeff1 exp1 units2 coeff2 exp2 Specific example: square-meters 1.0 0 square-yards 1.1960 0 " ##+############################################### ## ADDITONAL GUI INITIALIZATION section FOLLOWS. ##+############################################### ##+###################################################### ## Put the units ID names in the listbox, and ## set the conversion factors --- aRcoef and aRexp --- ## for each unit --- how many units in a 'square-meter'. ## ## Use about 10 significant digits. ## ## We put this list in alphabetical order by units name. ## ## Reference for length conversion: ## http://www.rapidtables.com/convert/length/index.htm ## See links at bottom of that page for more precise ## conversion factors. ## ## Note that: ## Squares of length factors for converting a meter to ## a length in another unit ## yield ## area-conversion factors that convert a square-meter ## into the square of that unit. ## ## Other references specific to area conversion: ## http://www.onlineconversion.com/area.htm ## http://www.thecalculatorsite.com/conversions/area.php ## https://www.calculateme.com/Area/ ## https://converterin.com/area.html ## http://coolconversion.com/area-unit-conversion ## ## NOTE: The conversion factors below may not be to the ## precision required to answer certain questions. ## In fact, there may be some gross errors in one or ## more of these conversion factors. Testing should be ## done to determine the accuracy of these factors and ## adjust them as needed. ##+###################################################### .fRright.listbox insert end "acres" set aRcoef(acres/square-meter) 0.00024710538146717 set aRexp(acres/square-meter) 0 .fRright.listbox insert end "hectares" set aRcoef(hectares/square-meter) 0.0001 set aRexp(hectares/square-meter) 0 .fRright.listbox insert end "square-centimeters" set aRcoef(square-centimeters/square-meter) 1.0 set aRexp(square-centimeters/square-meter) 4 .fRright.listbox insert end "square-inches" set aRcoef(square-inches/square-meter) 1550.003099 set aRexp(square-inches/square-meter) 0 .fRright.listbox insert end "square-feet" set aRcoef(square-feet/square-meter) 10.76391041 set aRexp(square-feet/square-meter) 0 .fRright.listbox insert end "square-kilometers" set aRcoef(square-kilometers/square-meter) 1.0 set aRexp(square-kilometers/square-meter) -6 ## Reference: ## https://www.convertunits.com/from/meters/to/light+year ## coef(lightyears/meter) = 1.0570008340246 ## exp(lightyears/meter) = -16 ## Square this conversion factor to get area conversion factor. ## .fRright.listbox insert end "square-lightyears" # set aRcoef(square-lightyears/square-meter) 1.117250763 set aRcoef(square-lightyears/square-meter) 1.1172508764051 set aRexp(square-lightyears/square-meter) -32 .fRright.listbox insert end "square-meters" set aRcoef(square-meters/square-meter) 1.0 set aRexp(square-meters/square-meter) 0 .fRright.listbox insert end "square-micrometers" set aRcoef(square-micrometers/square-meter) 1.0 set aRexp(square-micrometers/square-meter) 12 .fRright.listbox insert end "square-miles" set aRcoef(square-miles/square-meter) 0.386102168 set aRexp(square-miles/square-meter) -6 .fRright.listbox insert end "square-millimeters" set aRcoef(square-millimeters/square-meter) 1.0 set aRexp(square-millimeters/square-meter) 6 .fRright.listbox insert end "square-nanometers" set aRcoef(square-nanometers/square-meter) 1.0 set aRexp(square-nanometers/square-meter) 18 .fRright.listbox insert end "square-yards" set aRcoef(square-yards/square-meter) 1.195990046 set aRexp(square-yards/square-meter) 0 ##+######################################################################## ## GET (four) INPUT ARGUMENT VALUES, if any. If none, set defaults. ## ## (Use these 4 to set initial variable values --- ## CHOSEunits1 ENTRYcoef1 ENTRYexp1 CHOSEunits2 -- and set ENTRYcoef2 and ENTRYexp2) ##+######################################################################## if {$argc == 4} { ## If initial values are passed as ARGUMENTS to this script, they must be ## CHOSEunits1 ENTRYcoef1 ENTRYexp1 CHOSEunits2 set CHOSEunits1 [lindex $argv 0] set ENTRYcoef1 [lindex $argv 1] set ENTRYexp1 [lindex $argv 2] set CHOSEunits2 [lindex $argv 3] # area_update "2from1" } else { ## If no area(s) have been supplied, use DEFAULTS for ## CHOSEunits1 ENTRYcoef1 ENTRYexp1 CHOSEunits2 ENTRYcoef2 ENTRYexp2 set CHOSEunits1 "square-meters" set ENTRYcoef1 "1.0" set ENTRYexp1 "0" set CHOSEunits2 "square-yards" # area_update "2from1" } ##+######################################################################## ## Show the initial units1 and units2 on the GUI. ##+######################################################################## .fRleft.fRunits1.txtUNITS1 delete 1.0 end .fRleft.fRunits1.txtUNITS1 insert end "$CHOSEunits1" .fRleft.fRunits2.txtUNITS2 delete 1.0 end .fRleft.fRunits2.txtUNITS2 insert end "$CHOSEunits2" ##+#################################### ## Set one of the 2 radiobuttons 'on'. ## If the user selects a listbox selection, ## this variable is used to determine whether ## 'Units1' or 'Units2' is to be assigned ## the chosen units. ##+#################################### set RADVARunits1or2 "units2" ##+################################################################ ## Set a decimal format to use for the coefficient result ## in the 'CONVERTtyp' calculations in procs 'area_update', ## 'adjust_values1' and 'adjust_values2'. ## Allows for consistent floating format. ## NOTE: ## It may be helpful to make the precision an option on the GUI. ##+################################################################ # set FORMATforCOEF "%7.4f" set FORMATforCOEF "%10.8f" # set FORMATforCOEF "%0.8f" # set FORMATforCOEF "%0.4f" ##+#################################### ## Set an initial message to the user. ##+#################################### advise_user \ "** To change units, choose a 'units1' or 'units2' radiobutton and ** select from listbox to set a Unit of Measure. Then ... ** Use a 'Calc' button to assure that the two versions of the ** area are equivalent."