#!/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: tkWeightConvertSelect.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 WEIGHT ## in one type of units to another type of units. ## ## The GUI allows for showing a single weight in two ## different units of measure. ## ## The GUI allows the user to pick a pair of units from ## a list of units. Examples: grams, kilograms, ounces, ## pounds, etc. ## ## This converter is intended to be able to convert weights chosen ## from a huge range of sizes: from the weight of a sun or a galaxy ## to the weight of a hydrogen atom or a sub-atomic particle like ## an electron. ## ## Estimates of the weight of the sun (in our solar system) are ## on the order of 2.0 times 10 to the 30th kilograms. So the ## weight of a galaxy with about a million suns would be on the ## order of 10 to the (30 + 6 =) 36th kilograms. ## ## Estimates of the weight of a hydrogen atom are on the order of ## 1.7 times 10 to the -24 grams --- or 1.7 times 10 to the ## -27th kilograms. Sub-atomic particles are roughly 1,000 times ## smaller --- less than 10 to the -30th kilograms. ## ## In order to be able to handle such huge ranges, in the various ## units, the GUI allows for specifying the weight (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 weight value --- by clicking on a 'UseIt' button. ## Both forms of the weight, 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 WEIGHT CONVERTER-SELECTOR utility. ## ##+################# ## THE GUI WIDGETS: ## ## The options available to the user are indicated by ## the following 'sketch' of the GUI: ## ## FRAMEnames ## VVVVVVVVVV ## ----------------------------------------------------------------------------------- ## tkWeight - 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 | grams | ## [in a text widget] | kilograms | ## .fRleft.fRvalues1 Coefficient: ___________ Exponent: {+}___{-} | ... | ## | ... | ## .fRleft.fRunits2 Units2:[selected units displayed here] O units2 | ounces | ## [in a text widget] | pounds | ## .fRleft.fRvalues2 Coefficient: ____________ Exponent: {+}___{-} | ... | ## | ... | ## | ... | ## | ... | ## | ... | ## | etc. | ## |<----------------------->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 ## - 2 text widgets ## - 4 entry 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 weight 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 weight in the two user-selected units. ## ## Sample output string: kilograms 1.0 0 grams 1.000000 3 ## ##+##################################################################### ## CALL FORMAT: ## ## .../tkWeightConvertSelect.tk [units1 coef1 exp1 units2] ## ## OR ## we could eventually ## use environment vars 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/tkWeightConvertSelect.tk` ## or ## TEMP=`$FEDIR_TKGUIS/tkWeightConvertSelect.tk grams 6.0 0 ounces` ## ## (The coef2 and exp2 values are computed from coef1 and exp1 and units1 ## in terms of units2.) ## ## OR if we implement initialization of the arguments via environment variables ## like the following: ## ## 2) COEF1=6.0 ## EXP1=0 ## UNITS1="grams" ## UNITS2="ounces" ## export COEF1 EXP1 UNITS1 UNITS2 ## ## TEMP=`$FEDIR_TKGUIS/tkWeightConvertSelect.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 buttons 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. ## ## - 'weight_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 'weight_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 2017aug24 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 ## 'weight_update' proc to support added ## comments on derivation of the ## weight conversion formulas. ##+############################################################################ ##+################################# ## SET THE TOP WINDOW NAME. ##+################################# wm title . "tkWeight - Converter / Selector" wm iconname . "WeightConvert" # 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 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 15 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 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 ##+####################################################################### ## 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(buttonCLEAR2) \ $aRtext(buttonCALC1from2) $aRtext(buttonCLEAR1)"] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 7 widgets x 4 pixels/widget for borders/padding ## for buttons and labels. set minWinWidthPx [expr {36 + $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 2 'Calc' BUTTONS and 2 'Clear' BUTTONS. ## 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 {weight_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 {weight_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 21 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 \ -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 weights 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 weights 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 weights 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 weights 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. ## ## 'weight_update' - called by 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 button 'Clear2' ## 'clear_values1' - called by button 'Clear1' ## ## 'edit_inputs' - called by the 'weight_update' proc. ## 'decimal_check' - called by the 'edit_inputs' proc. ## ## 'normalize_coef_exp' - called by the 'weight_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: button1-release binding on the listbox widget ##+################################################################ proc listbox_select_units {} { global RADVARunits1or2 CHOSEunits1 CHOSEunits2 \ 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 weights 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 weights not showing as equivalent. if {"$ENTRYcoef1" != ""} { set ENTRYcoef2 "" set ENTRYexp2 "0" } } } ## END OF PROC 'listbox_select_units' ##+##################################################################### ## PROC: 'weight_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 ENTRYcoef that is computed could be normalized to be ## between 1.0 and 9.999... ## ## The ENTRYexp that is computed can be any integer --- ## negative, zero, or positive. ## ## CALLED BY: the 'Calc 2from1' or 'Calc 1from2' buttons. ##+##################################################################### proc weight_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 --- ## either '2from1' or '1from2'. #################################################### eval set CoefUnits1PerStd \$aRcoef($CHOSEunits1/kilogram) eval set ExpUnits1PerStd \$aRexp($CHOSEunits1/kilogram) eval set CoefUnits2PerStd \$aRcoef($CHOSEunits2/kilogram) eval set ExpUnits2PerStd \$aRexp($CHOSEunits2/kilogram) ################################################################### ## If CONVERTtype = '2from1', use CHOSEunits1 ENTRYcoef1 ENTRYexp1 ## and CHOSEunits2 to calculate ENTRYcoef2 and ENTRYexp2. ## ## Note that ENTRYcoef1 is weight in units1. ## To convert ENTRYcoef1 to units2 in ENTRYcoef2, ## 1) we can convert ENTRYcoef1 to 'standard' units (kilograms) by ## dividing by CoefUnits1PerStd, ## 2) we convert 'standard' units (kilograms) 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 (kilograms) ## by subtracting ExpUnits1PerStd, ## 2) we convert 'standard' units (kilograms) 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 weight in units2. ## To convert ENTRYcoef2 to units1 in ENTRYcoef1, ## 1) we can convert ENTRYcoef2 to 'standard' units (kilograms) by ## dividing by CoefUnits2PerStd, ## 2) we convert 'standard' units (kilograms) 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 (kilograms) ## by subtracting ExpUnits2PerStd, ## 2) we convert 'standard' units (kilograms) 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 'weight_update' ##+##################################################################### ## PROC: 'adjust_values1' ##+##################################################################### ## PURPOSE: Uses the 'plusORminus1' 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 'plusORminus1' 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 'weight_update' proc ##+##################################################################### proc edit_inputs {CONVERTtype} { # global CHOSEunits1 CHOSEunits2 global ENTRYcoef1 ENTRYexp1 ENTRYcoef2 ENTRYexp2 ## We could do without the following 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 weight $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYexp1" == ""} { popup_msgVarWithScroll .topErr "Exponent for UNITS1 weight $MSGblank" +10+10 set EDITcode 1 return } ########################################################## ## Check that ENTRYexp1 is integer. ########################################################## if {![string is integer -strict "$ENTRYexp1"]} { popup_msgVarWithScroll .topErr "Exponent for weight 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 weight $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 weight $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYexp2" == ""} { popup_msgVarWithScroll .topErr "Exponent for UNITS2 weight $MSGblank" +10+10 set EDITcode 1 return } ########################################################## ## Check that ENTRYexp2 is integer. ########################################################## if {![string is integer -strict "$ENTRYexp2"]} { popup_msgVarWithScroll .topErr "Exponent for weight 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 weight $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 ## 'weight_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 \ " ************ tkWeight - Converter / Selector Utility ********* This Tk GUI script provides a GUI for CONVERTING a WEIGHT in one unit of measure to the weight in a second unit of measure. The weight 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, typically 'normalized' between 1.0 and 1000.0. The exponents can be positive integers or zero or negative integers --- in other words, an 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 two 'units1' and 'units2' radiobutton widgets on the GUI --- and via the listbox. Click on a radiobutton and then click on a unit-of-measure in the listbox. STEP 2: (calculating one weight 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 weights, this GUI is also for SELECTING the weight --- expressed as both the Units1 coefficient and exponent AND the Units2 coefficient and exponent. When the user clicks on the 'UseIt' button, the weight is written as a text string to 'stdout' in both forms. Sample output: grams 6.0000 0 kilograms 0.00600 0 Thus this utility Tk script can be called from within a shell script or from within a Tcl-Tk script to get a weight. *********************************************** 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/tkWeightConvertSelect.tk` OR in the form TEMP=`\$SCRIPT_LOC/tkWeightConvertSelect.tk units1 coef1 exp1 units2` with specific values such as TEMP=`\$SCRIPT_LOC/tkWeightConvertSelect.tk grams 6.0 0 ounces` The coef2 and exp2 values can then be computed from units1, coef1, exp1, 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 A WEIGHT CONVERTER-SELECTOR by calculating and outputting a currently specified weight to 'stdout' (standard output), when the 'UseIt' button is clicked. Example output string format: units1 coeff1 exp1 units2 coeff2 exp2 Specific example: grams 6.000000 0 kilograms 0.00600 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 'kilogram'. ## NOTE: ## 'kilogram' will be our 'base' unit of measure. ## (There are 1000 grams in a kilogram.) ## ## Use about 10 significant digits for the coefficients. ## ## We put this list in alphabetical order by to-units name. ## Example: ## grams ## kilograms ## ounces ## pounds ## ## References specific to weight conversion: ## http://coolconversion.com/weight-unit-conversion ## https://www.calculateme.com/Weight/ ## https://www.scalesgalore.com/wtconverter.htm ## http://www.onlineconversion.com/weight.htm ## http://www.thecalculatorsite.com/conversions/massandweight.php ## https://converterin.com/weight-mass.html ## ## 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. ##+###################################################### ## Most of the conversion factors below were found using ## http://coolconversion.com/weight-unit-conversion ##+###################################################### .fRright.listbox insert end "centigrams" set aRcoef(centigrams/kilogram) 100000.0 set aRexp(centigrams/kilogram) 0 .fRright.listbox insert end "drams" set aRcoef(drams/kilogram) 564.38339119329 set aRexp(drams/kilogram) 0 .fRright.listbox insert end "grains" set aRcoef(grains/kilogram) 15432.358352941 set aRexp(grains/kilogram) 0 .fRright.listbox insert end "grams" set aRcoef(grams/kilogram) 1000.0 set aRexp(grams/kilogram) 0 .fRright.listbox insert end "kilograms" set aRcoef(kilograms/kilogram) 1.0 set aRexp(kilograms/kilogram) 0 .fRright.listbox insert end "megagrams" set aRcoef(megagrams/kilogram) 0.001 set aRexp(megagrams/kilogram) 0 .fRright.listbox insert end "micrograms" set aRcoef(micrograms/kilogram) 1000000000 set aRexp(micrograms/kilogram) 0 .fRright.listbox insert end "milligrams" set aRcoef(milligrams/kilogram) 1000000 set aRexp(milligrams/kilogram) 0 .fRright.listbox insert end "ounces" set aRcoef(ounces/kilogram) 35.27396194958 set aRexp(ounces/kilogram) 0 .fRright.listbox insert end "pounds" set aRcoef(pounds/kilogram) 2.2046226218488 set aRexp(pounds/kilogram) 0 .fRright.listbox insert end "ton-metric" set aRcoef(ton-metric/kilogram) 0.001 set aRexp(ton-metric/kilogram) 0 .fRright.listbox insert end "ton-short-US" set aRcoef(ton-short-US/kilogram) 0.0011023113109244 set aRexp(ton-short-US/kilogram) 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] # weight_update "2from1" } else { ## If no weight(s) have been supplied, use DEFAULTS for ## CHOSEunits1 ENTRYcoef1 ENTRYexp1 CHOSEunits2 ENTRYcoef2 ENTRYexp2 set CHOSEunits1 "pounds" set ENTRYcoef1 "170.0" set ENTRYexp1 "0" set CHOSEunits2 "kilograms" # weight_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 value 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 'weight_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 ** weight are equivalent."