#!/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: tkDensityConvertSelect.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 DENSITY ## in one type of units to another type of units. ## ## The GUI allows for showing a single density in two ## different units of measure. ## ## (NOTE: ## DENSITY is a ratio of weight (or mass) over volume. WEIGHT is a ## quite variable quantity. It depends on the planet (or gravitational body) ## that one is considering and even the altitude relative to the center ## of that body. ## ## MASS is independent of considerations like that. It may be helpful ## to think of the numerator of the density as being a mass rather than ## a weight. But our daily experiences involve weight rather than mass. ## ## When one sees two different units of weight (pounds and kilograms, for ## example), it may help to think of two masses on a weight balance ## that are in balance on a particular planet (like Earth) and at a ## particular altitude (like sea level). ## ## On one side of the balance, we have a collection of weights in ## pounds and fractions of a pound. On the other side of the balance, ## we have weights in kilograms and fractions of a kilogram. Let us ## consider the collection of pounds-weights as the given weight. ## ## In that case, this GUI is determining the collection of kilogram-weights ## that will balance the pound-weights. And these will balance no matter ## what planet we are on and no matter what altitude on that planet. ## ## In other words, although we may think in terms of pounds being a ## weight (force) and kilograms being a weight (force), it may be ## helpful to think of balancing masses rather than weights.) ## ## The GUI allows the user to pick a pair of density units --- ## from units in TWO 'listbox' widgets. ## ## NOTE: ## Density is a ratio of weight-or-mass-over-volume. If we imagine ## allowing for 10 different units for weight-or-mass and 8 different ## units for volume, this results in 10 x 8 = 80 different units ## of density to choose from. ## ## Rather than using a single listbox with about 80 entries (or more), ## we use a 'weight-or-mass' listbox and a 'volume' listbox with about ## 10 to 20 entries each. ## ## Examples of weight-or-mass units: pounds, kilograms, grams, etc. ## ## Examples of volume units (alphabetically): cubic-centimeters, ## cubic-inches, cubic-kilometers, cubic-meters, etc. ## ## This converter is intended to be able to convert densities chosen ## from a huge range of sizes: densities on the order of a ## a ton per cubic-nanometer --- or much 'frothier' --- to extremely slow ## densities, on the order of a microgram per cubic-lightyear. ## ## Estimates of the diameter of the 'observable' universe are on the ## order of 1.0 times 10 to the 11th lightyears --- which is about ## 10 to the 27th meters. This GUI allows for dealing with densities ## involving volumes as large as the volume of the universe --- like ## the density of a single hydrogen atom in the volume of the ## 'observable' universe. ## ## Similarly for extremely high densities. ## ## Estimates of the diameter of a hydrogen atom is on the order of ## 1.0 times 10 to the -11 meters. And the size of a subatomic ## particle is at least 1000 times smaller --- so less than ## 10 to the -14 meters. ## ## This GUI can deal with densities such a ton in the estimaged volume of ## a sub-atomic particle. ## ## In order to be able to handle such huge ranges, in the various ## units, the GUI allows for specifying the density (in either ## of the pair of user-selected units) in 'scientific notation': ## a COEFFICIENT (say, between 1.00000000 and 10000.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 density value --- by clicking on a 'UseIt' button. ## Both forms of the density, 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 DENSITY CONVERTER-SELECTOR utility. ## ##+################# ## THE GUI WIDGETS: ## ## The options available to the user are indicated by ## the following 'sketch' of the GUI: ## ## FRAMEnames ## VVVVVVVVVV ## ----------------------------------------------------------------------------------- ## tkDensity - 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 2 scrollable listboxes below ## in this '.fRleft' frame.] are in sub-frames --- '.fRmass' and ## .fRvolume --- in this '.fRright' frame.] ## ## |----------------A |-------------------A ## .fRleft.fRunits1 Units1:[selected units displayed here] O units1 | ... | | cubic-centimeters | ## [in 2 text widgets separated by a 'per' label] | ... | | ... | ## | ... | | ... | ## .fRleft.fRvalues1 Coefficient: ___________ Exponent: {+}___{-} | grams | | cubic-inches | ## | ... | | ... | ## .fRleft.fRunits2 Units2:[selected units displayed here] O units2 | kilograms | | cubic-kilometers | ## [in 2 text widgets separated by a 'per' label] | ... | | ... | ## | ... | | ... | ## .fRleft.fRvalues2 Coefficient: ____________ Exponent: {+}___{-} | ... | | cubic-meters | ## | pounds | | ... | ## | ... | | ... | ## | tons-US | | ... | ## | ... | | ... | ## | ... | | cubic-yards | ## |<-------------->V |<----------------->V ## ## [We put the units-of-measure in the 2 listboxes in alphabetical order ## so that the user can relatively easily locate a unit-of-measure ## in what may eventually be rather long lists.] ## ------------------------------------------------------------------- ## ## 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 text widgets ## - 4 entry widgets ## - 2 listbox widgets (each with scrollbars) ## - 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 2 radiobuttons and ## the 2 listboxes of the GUI. ## ## OUTPUT: The converted density values are displayed in a pair of ## entry widgets --- for computed coefficient and exponent. ## ## If the user clicks on the 'UseIt' button, ## the coefficient and exponent values are passed to stdout ## as a string giving density in the two user-selected units. ## ## Sample output string: ## pounds cubic-foot 1.0 0 pounds cubic-inch 0.000578704 0 ## ##+##################################################################### ## CALL FORMAT: ## ## .../tkDensityConvertSelect.tk [unitsM1 unitsV1 coef1 exp1 unitsM2 unitsV2] ## ## 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/tkDensityConvertSelect.tk` ## or ## TEMP=`$FEDIR_TKGUIS/tkDensityConvertSelect.tk pounds cubic-foot 6.0 1 pounds cubic-inch` ## ## (The coef2 and exp2 values are computed from coef1 and exp1 --- ## according to the pair unitsM1,unitsV1 and the pair unitsM2,unitsV2.) ## ## OR if we ever implement initializing the GUI via environment variables: ## ## 2) COEF1=6.0 ## EXP1=1 ## UnitsM1="pounds" ## UnitsV1="cubic-foot" ## UnitsM2="pounds" ## UnitsV2="cubic-inch" ## export COEF1 EXP1 UnitsM1 UnitsV1 UnitsM2 UnitsV2 ## ## TEMP=`$FEDIR_TKGUIS/tkDensityConvertSelect.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. ## ## - 'density_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 'density_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 2017sep01 Started version for the FE 'tkGooies' ## system, on Linux. ## Changed by: Blaise Montandon 2017 ##+############################################################################ ##+################################# ## SET THE TOP WINDOW NAME. ##+################################# wm title . "tkDensity - Converter / Selector" wm iconname . "DensityConvert" # 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. ## ## We could use an alternate naming convention for these variables ## --- to be more like the naming used for variables below. ## Example: BGCOLOR_entry instead of entryBKGD. 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) ##+####################################################################### ## 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 19 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(labelUnitsM1) "Units1:" set aRtext(labelUnitsV1) " per " set aRtext(labelUnitsM2) "Units2:" set aRtext(labelUnitsV2) " per " ## 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 2 listbox widgets (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. ## ## '.fRright.fRmass' - to contain a listbox widget with x,y scrollbars. ## '.fRright.fRvolume' - to contain a listbox widget with x,y scrollbars. ##+#################################################################### ##+#################################################################### ## 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 frame .fRright.fRmass -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRright.fRvolume -relief $RELIEF_frame -borderwidth $BDwidthPx_frame ##+######################################################## ## 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 both \ -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 pack .fRright.fRmass \ .fRright.fRvolume \ -side left \ -anchor nw \ -fill both \ -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 {density_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 {density_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_forUnitsM 6 set TextWidthChars_forUnitsM 15 set LabelWidthChars_forUnitsV 4 set TextWidthChars_forUnitsV 19 set EntryWidthChars_forCoef 16 set EntryWidthChars_forExp 5 ##+######################################################## ## IN THE '.fRleft.fRunits1' frame -- ## DEFINE 2 pairs of LABEL and TEXT widgets (to display ## weight-or-mass and volume units) and 1 RADIOBUTTON. ## THEN PACK THEM. ##+######################################################## label .fRleft.fRunits1.labelUnitsM1 \ -text "$aRtext(labelUnitsM1)" \ -font fontTEMP_label \ -width $LabelWidthChars_forUnitsM \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits1.txtUnitsM1 \ -height 1 \ -width $TextWidthChars_forUnitsM \ -font fontTEMP_text \ -wrap none \ -borderwidth $BDwidthPx_text \ -relief $RELIEF_text \ -bg $textBKGD label .fRleft.fRunits1.labelUnitsV1 \ -text "$aRtext(labelUnitsV1)" \ -font fontTEMP_label \ -width $LabelWidthChars_forUnitsV \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits1.txtUnitsV1 \ -height 1 \ -width $TextWidthChars_forUnitsV \ -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.labelUnitsM1 \ .fRleft.fRunits1.txtUnitsM1 \ .fRleft.fRunits1.labelUnitsV1 \ .fRleft.fRunits1.txtUnitsV1 \ -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 2 pairs of LABEL and TEXT widgets (to display ## weight-or-mass and volume units) and 1 RADIOBUTTON. ## THEN PACK THEM. ##+######################################################## label .fRleft.fRunits2.labelUnitsM2 \ -text "$aRtext(labelUnitsM2)" \ -width $LabelWidthChars_forUnitsM \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits2.txtUnitsM2 \ -height 1 \ -width $TextWidthChars_forUnitsM \ -font fontTEMP_text \ -wrap none \ -borderwidth $BDwidthPx_text \ -relief $RELIEF_text \ -bg $textBKGD label .fRleft.fRunits2.labelUnitsV2 \ -text "$aRtext(labelUnitsV2)" \ -width $LabelWidthChars_forUnitsV \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits2.txtUnitsV2 \ -height 1 \ -width $TextWidthChars_forUnitsV \ -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.labelUnitsM2 \ .fRleft.fRunits2.txtUnitsM2 \ .fRleft.fRunits2.labelUnitsV2 \ .fRleft.fRunits2.txtUnitsV2 \ -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.fRmass' frame - ## DEFINE-and-PACK 1 LISTBOX widget (with x,y scrollbars). ##+###################################################### listbox .fRright.fRmass.listboxM \ -width $initListboxWidthChars \ -height $initListboxHeightChars \ -font fontTEMP_listbox \ -borderwidth $BDwidthPx_listbox \ -relief $RELIEF_listbox \ -state normal \ -yscrollcommand ".fRright.fRmass.scrbary set" \ -xscrollcommand ".fRright.fRmass.scrbarx set" scrollbar .fRright.fRmass.scrbary \ -orient vertical -command ".fRright.fRmass.listboxM yview" scrollbar .fRright.fRmass.scrbarx \ -orient horizontal -command ".fRright.fRmass.listboxM xview" ## Pack the widgets in frame '.fRright.fRmass'. pack .fRright.fRmass.scrbary \ -side right \ -anchor e \ -fill y \ -expand 0 pack .fRright.fRmass.scrbarx \ -side bottom \ -anchor sw \ -fill x \ -expand 0 pack .fRright.fRmass.listboxM \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+###################################################### ## In the '.fRright.fRvolume' frame - ## DEFINE-and-PACK 1 LISTBOX widget (with x,y scrollbars). ##+###################################################### listbox .fRright.fRvolume.listboxV \ -width $initListboxWidthChars \ -height $initListboxHeightChars \ -font fontTEMP_listbox \ -borderwidth $BDwidthPx_listbox \ -relief $RELIEF_listbox \ -state normal \ -yscrollcommand ".fRright.fRvolume.scrbary set" \ -xscrollcommand ".fRright.fRvolume.scrbarx set" scrollbar .fRright.fRvolume.scrbary \ -orient vertical -command ".fRright.fRvolume.listboxV yview" scrollbar .fRright.fRvolume.scrbarx \ -orient horizontal -command ".fRright.fRvolume.listboxV xview" ## Pack the widgets in frame '.fRright.fRvolume'. pack .fRright.fRvolume.scrbary \ -side right \ -anchor e \ -fill y \ -expand 0 pack .fRright.fRvolume.scrbarx \ -side bottom \ -anchor sw \ -fill x \ -expand 0 pack .fRright.fRvolume.listboxV \ -side top \ -anchor nw \ -fill both \ -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.fRmass.listboxM "listboxM_select_units" bind .fRright.fRvolume.listboxV "listboxV_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 densities 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 densities 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 densities 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 densities may no longer be equivalent. set ENTRYcoef1 "" set ENTRYexp1 "0" } ##+##################################################################### ##+##################################################################### ## DEFINE PROCEDURES: ## ## 'listboxM_select_units' - called by a button1-release binding on ## the 'listboxM' widget. ## ## 'listboxV_select_units' - called by a button1-release binding on ## the 'listboxV' widget. ## ## 'density_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 'density_update' proc. ## 'decimal_check' - called by the 'edit_inputs' proc. ## ## 'normalize_coef_exp' - called by the 'density_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. ##+##################################################################### ##+##################################################################### ##+################################################################ ## PROC 'listboxM_select_units' ##+################################################################ ## PURPOSE: Sets some DISTANCE 'units' variables depending on the ## current setting of the radiobutton variable RADVARunits1or2. ## ## CALLED BY: binding on the 'listboxM' widget ##+################################################################ proc listboxM_select_units {} { global RADVARunits1or2 CHOSEunitsM1 CHOSEunitsM2 global ENTRYcoef1 ENTRYexp1 ENTRYcoef2 ENTRYexp2 set sel_index [ .fRright.fRmass.listboxM curselection ] if { $sel_index != "" } { set selectedUNITS [ .fRright.fRmass.listboxM get $sel_index ] } ## FOR TESTING: # puts "listboxM_select_units: selectedUNITS = $selectedUNITS" ######################################################## ## Load in the units name in a CHOSEunits variable and in ## a text widget. ######################################################## if {"$RADVARunits1or2" == "units1"} { set CHOSEunitsM1 "$selectedUNITS" .fRleft.fRunits1.txtUnitsM1 delete 1.0 end .fRleft.fRunits1.txtUnitsM1 insert end "$CHOSEunitsM1" ## 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 volumes not showing as equivalent. if {"$ENTRYcoef2" != ""} { set ENTRYcoef1 "" set ENTRYexp1 "0" } } if {"$RADVARunits1or2" == "units2"} { set CHOSEunitsM2 "$selectedUNITS" .fRleft.fRunits2.txtUnitsM2 delete 1.0 end .fRleft.fRunits2.txtUnitsM2 insert end "$CHOSEunitsM2" ## 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 volumes not showing as equivalent. if {"$ENTRYcoef1" != ""} { set ENTRYcoef2 "" set ENTRYexp2 "0" } } } ## END OF PROC 'listboxM_select_units' ##+################################################################ ## PROC 'listboxV_select_units' ##+################################################################ ## PURPOSE: Sets some VOLUME 'units' variables depending on the ## current setting of the radiobutton variable RADVARunits1or2. ## ## CALLED BY: binding on the 'listboxM' widget ##+################################################################ proc listboxV_select_units {} { global RADVARunits1or2 CHOSEunitsV1 CHOSEunitsV2 global ENTRYcoef1 ENTRYexp1 ENTRYcoef2 ENTRYexp2 set sel_index [ .fRright.fRvolume.listboxV curselection ] if { $sel_index != "" } { set selectedUNITS [ .fRright.fRvolume.listboxV get $sel_index ] } ## FOR TESTING: # puts "listboxV_select_units: selectedUNITS = $selectedUNITS" ######################################################## ## Load in the units name in a CHOSEunits variable and ## in a text widget. ######################################################## if {"$RADVARunits1or2" == "units1"} { set CHOSEunitsV1 "$selectedUNITS" .fRleft.fRunits1.txtUnitsV1 delete 1.0 end .fRleft.fRunits1.txtUnitsV1 insert end "$CHOSEunitsV1" ## 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 volumes not showing as equivalent. if {"$ENTRYcoef2" != ""} { set ENTRYcoef1 "" set ENTRYexp1 "0" } } if {"$RADVARunits1or2" == "units2"} { set CHOSEunitsV2 "$selectedUNITS" .fRleft.fRunits2.txtUnitsV2 delete 1.0 end .fRleft.fRunits2.txtUnitsV2 insert end "$CHOSEunitsV2" ## 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 volumes not showing as equivalent. if {"$ENTRYcoef1" != ""} { set ENTRYcoef2 "" set ENTRYexp2 "0" } } } ## END OF PROC 'listboxV_select_units' ##+##################################################################### ## PROC: 'density_update' ##+##################################################################### ## ## PURPOSE: If the argument to this proc is the string "2from1", ## use CHOSEunitsM1 CHOSEunitsV1 ENTRYcoef1 ENTRYexp1 and ## CHOSEunitsM2 CHOSEunitsV2 ## to compute ENTRYcoef2 and ENTRYexp2. ## AND ## If the argument to this proc is the string "1from2", ## use CHOSEunitsM2 CHOSEunitsV2 ENTRYcoef2 ENTRYexp2 and ## CHOSEunitsM1 CHOSEunitsV1 ## 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 density_update {CONVERTtype} { ## Dummy out this proc, for testing. # return ######################################################################## ## The variable 'CONVERTtype' should be the string "2from1" or "1from2". ######################################################################## global CHOSEunitsM1 CHOSEunitsV1 ENTRYcoef1 ENTRYexp1 \ CHOSEunitsM2 CHOSEunitsV2 ENTRYcoef2 ENTRYexp2 \ aRcoefM aRexpM aRcoefV aRexpV 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' or '1from2'. #################################################### eval set CoefDunits1PerDstd \$aRcoefM($CHOSEunitsM1/kilogram) eval set ExpDunits1PerDstd \$aRexpM($CHOSEunitsM1/kilogram) eval set CoefTstdPerTunits1 \$aRcoefV(cubicmeters/$CHOSEunitsV1) eval set ExpTstdPerTunits1 \$aRexpV(cubicmeters/$CHOSEunitsV1) eval set CoefDunits2PerDstd \$aRcoefM($CHOSEunitsM2/kilogram) eval set ExpDunits2PerDstd \$aRexpM($CHOSEunitsM2/kilogram) eval set CoefTstdPerTunits2 \$aRcoefV(cubicmeters/$CHOSEunitsV2) eval set ExpTstdPerTunits2 \$aRexpV(cubicmeters/$CHOSEunitsV2) ################################################################### ## If CONVERTtype = '2from1', use ## ENTRYcoef1 ENTRYexp1 and the pair CHOSEunitsM1,CHOSEunitsV1 and ## the pair CHOSEunitsM2,CHOSEunitsV2 to calculate ## ENTRYcoef2 and ENTRYexp2. ## ## Note that ENTRYcoef1 is density in unitsM1/unitsV1. ## To convert ENTRYcoef1 to unitsM2/unitsV2 in ENTRYcoef2, ## 1) we can convert ENTRYcoef1 to 'standard' units (meter/second) by ## dividing by CoefDunits1PerDstd and dividing by CoefTstdPerTunits1 ## --- i.e. divide by (CoefDunits1PerDstd * CoefTstdPerTunits1), ## 2) we convert 'standard' units (meter/second) to unitsM2/unitsV2 by ## multiplying by CoefDunits2PerDstd and multiplying by CoefTstdPerTunits2 ## --- i.e multiply by (CoefDunits2PerDstd * CoefTstdPerTunits2). ## ## To convert the exponent ENTRYexp1 to unitsM2/unitsV2 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 (meter/second) ## by subtracting ExpDunits1PerDstd and subtracting ExpTstdPerTunits1, ## 2) we convert 'standard' units meter/second to unitsM2/unitsV2 ## by adding ExpDunits2PerDstd and adding ExpTstdPerTunits2. ################################################################### if {"$CONVERTtype" == "2from1"} { set ENTRYcoef2 [expr {$ENTRYcoef1 * ($CoefDunits2PerDstd * $CoefTstdPerTunits2) / ($CoefDunits1PerDstd * $CoefTstdPerTunits1)}] set ENTRYexp2 [expr {($ENTRYexp1 + $ExpDunits2PerDstd + $ExpTstdPerTunits2) - ($ExpDunits1PerDstd + $ExpTstdPerTunits1)}] # normalize_coef_exp ENTRYcoef2 ENTRYexp2 set ENTRYcoef2 [ format "$FORMATforCOEF" $ENTRYcoef2] } ################################################################### ## If CONVERTtype = '1from2', use ## ENTRYcoef2 ENTRYexp2 and the pair CHOSEunitsM2,CHOSEunitsV2 and ## the pair CHOSEunitsM1,CHOSEunitsV1 to calculate ## ENTRYcoef1 and ENTRYexp1. ## ## Note that ENTRYcoef2 is density in unitsM2/unitsV2. ## To convert ENTRYcoef2 to unitsM1/unitsV1 in ENTRYcoef1, ## 1) we can convert ENTRYcoef2 to 'standard' units (meter/second) by ## dividing by CoefDunits2PerDstd and dividing by CoefTstdPerTunits2 ## --- i.e. divide by (CoefDunits2PerDstd * CoefTstdPerTunits2), ## 2) we convert 'standard' units (meter/second) to unitsM1/unitsV1 by ## multiplying by CoefDunits1PerDstd and multiplying by CoefTstdPerTunits1 ## --- i.e. multiply by (CoefDunits1PerDstd * CoefTstdPerTunits1). ## ## To convert the exponent ENTRYexp2 to unitsM1/unitsV1 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 (meter/second) ## by subtracting ExpDunits2PerDstd and subtracting ExpTstdPerTunits2, ## 2) we convert 'standard' units meter/second to unitsM1/unitsV1 ## by adding ExpDunits1PerDstd and adding ExpTstdPerTunits1. ################################################################### if {"$CONVERTtype" == "1from2"} { set ENTRYcoef1 [expr {$ENTRYcoef2 * ($CoefDunits1PerDstd * $CoefTstdPerTunits1) / ($CoefDunits2PerDstd * $CoefTstdPerTunits2)}] set ENTRYexp1 [expr {($ENTRYexp2 + $ExpDunits1PerDstd + $ExpTstdPerTunits1) - ($ExpDunits2PerDstd + $ExpTstdPerTunits2)}] # normalize_coef_exp ENTRYcoef1 ENTRYexp1 set ENTRYcoef1 [ format "$FORMATforCOEF" $ENTRYcoef1] } } ## END of proc 'density_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 'density_update' proc ##+##################################################################### proc edit_inputs {CONVERTtype} { 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 density $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYexp1" == ""} { popup_msgVarWithScroll .topErr "Exponent for UNITS1 density $MSGblank" +10+10 set EDITcode 1 return } ########################################################## ## Check that ENTRYexp1 is integer. ########################################################## if {![string is integer -strict "$ENTRYexp1"]} { popup_msgVarWithScroll .topErr "Exponent for density 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 density $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 density $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYexp2" == ""} { popup_msgVarWithScroll .topErr "Exponent for UNITS2 density $MSGblank" +10+10 set EDITcode 1 return } ########################################################## ## Check that ENTRYexp2 is integer. ########################################################## if {![string is integer -strict "$ENTRYexp2"]} { popup_msgVarWithScroll .topErr "Exponent for density 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 density $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 CHOSEunitsM1 CHOSEunitsV1 ENTRYcoef1 ENTRYexp1 \ CHOSEunitsM2 CHOSEunitsV2 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 \ "$CHOSEunitsM1 $CHOSEunitsV1 $ENTRYcoef1 $ENTRYexp1 \ $CHOSEunitsM2 $CHOSEunitsV2 $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 ## 'density_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 \ " ************ tkDensity - Converter / Selector Utility ********* This Tk GUI script provides a GUI for CONVERTING a DENSITY in one unit of measure to the density in a second unit of measure. The densities in both 'Units1' and 'Units2' are 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. Using scientific notation allows for dealing with densities over a huge range of magnitudes --- from a 'microgram/cubic-lightyear' (or less) to a 'megagram/cubic-micrometer' (or more) --- i.e. from the average density of the universe (or less) to the density of the Earth's core (or more) --- such as the density of the core of a 'neutron star' or a 'black hole'. 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. ****************** MASS versus WEIGHT ****************** DENSITY is a ratio of weight (or mass) over volume. WEIGHT is a quite variable quantity. It depends on the planet (or gravitational body) that one is considering and even the altitude relative to the center of that body. MASS is independent of considerations like that. It may be helpful to think of the numerator of the density as being a mass rather than a weight. But our daily experiences involve weight rather than mass. When one sees two different units of weight (pounds and kilograms, for example), it may help to think of two masses on a weight balance that are in balance on a particular planet (like Earth) and at a particular altitude (like sea level). On one side of the balance, we have a collection of weights in pounds and fractions of a pound. On the other side of the balance, we have weights in kilograms and fractions of a kilogram. Let us consider the collection of pounds-weights as the given weight. In that case, this GUI can determine the collection of kilogram-weights that will balance the pound-weights. And these will balance no matter what planet we are on and no matter what altitude on that planet. In other words, although we may think in terms of pounds being a weight (a force) and kilograms being a weight (a force), it may be helpful to think of dealing with masses rather than weights --- especially in extra-terrestrial situations. *********************************************** HOW DENSITY 'units1' AND 'units2' ARE SPECIFIED on this GUI *********************************************** Density is a ratio of weight(or mass)-over-volume. If we imagine allowing for 10 different units-of-measure for weight-or-mass and 12 different units-of-measure for volume, this results in as many as 10 x 12 = 120 different units-of-measure of density to choose from. Rather than using a single listbox with about 120 entries (or more), we use a 'weight-or-mass' listbox and a 'volume' listbox with about 10 to 20 entries each. Examples of weight-or-mass units: pounds, kilograms, grams, ounces, tons, etc. Examples of volume units (alphabetically): cubic-centimeters, cubic-inches, cubic-kilometers, cubic-meters, gallons, liters, quarts, etc. Note: We allow for 'liquid' volumes as well as 'cubic' volumes. ******************* 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 2 listboxes. Click on a radiobutton and then click on a unit-of-measure in the 'weight-or-mass' listbox and/or in the 'volume' listbox. STEP 2: (calculating one density 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 densities, this GUI can also be used for SELECTING the density --- expressed as both the Units1 coefficient and exponent AND the Units2 coefficient and exponent. When the user clicks on the 'UseIt' button, the density is written as a text string to 'stdout' in both forms --- as two 'quadlets'. Sample output: kilograms cubic-centimeter 1.0 0 kilograms cubic-meter 1.00000 6 Thus this utility Tk script can be called from within a shell script or from within a Tcl-Tk script to get a density. (Note: The output above indicates that you would have to have 10^6 kilograms in a cubic-meter to give the same density as a kilogram in a cubic-centimeter.) *********************************************** 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/tkDensityConvertSelect.tk` OR in the form TEMP=`\$SCRIPT_LOC/tkDensityConvertSelect.tk unitsM1 unitsV1 coef1 exp1 unitsM2 unitsV2` with specific values such as TEMP=`\$SCRIPT_LOC/tkDensityConvertSelect.tk kilograms cubic-meter 6.0 1 grams cubic-centimeter` The coef2 and exp2 values can then be computed from units1, coef1, exp1, and units2 by a click on a 'Calc' button --- and the two 'quadlets' can be passed to the TEMP variable by a click on the 'UseIt' button. 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 DENSITY CONVERTER-SELECTOR by calculating and outputing a currently specified density to 'stdout' (standard output), when the 'UseIt' button is clicked. Example output string format: unitsM1 unitsV1 coeff1 exp1 unitsM2 unitsV2 coeff2 exp2 Specific example: kilograms cubic-centimeter 1.0 0 kilograms cubic-meter 1.00000 6 " ##+############################################### ##+############################################### ## ADDITONAL GUI INITIALIZATION section FOLLOWS. ##+############################################### ##+############################################### ##+###################################################### ## Put the units names in 'listboxM' (the weight-or-mass listbox), ## and set the conversion factors --- aRcoefM and aRexpM --- ## for each weight-or-mass unit --- how many units in a 'kilogram'. ## ## NOTE: 'kilogram' will be our 'base' unit of weight-or-mass measure. ## ## Use about 10 significant digits for the coefficients. ## ## We put this list in alphabetical order by units name. ## Example: ## grams ## kilograms ## pounds ## tons-US ## ## Some references for weight-or-mass 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. ##+###################################################### ## To test the density conversion of this GUI, here are ## some references for density conversion: ## http://www.onlineconversion.com/density.htm ## http://www.onlineconversion.com/density_common.htm ## http://www.onlineconversion.com/density_all.htm ## http://www.unitconversion.org/unit_converter/density.html ## http://www.engineeringtoolbox.com/density-converter-d_1038.html ## http://www.unit-conversion.info/density.html ## https://www.calculatorsoup.com/calculators/conversions/density.php ##+###################################################### .fRright.fRmass.listboxM insert end "centigrams" set aRcoefM(centigrams/kilogram) 100000.0 set aRexpM(centigrams/kilogram) 0 .fRright.fRmass.listboxM insert end "drams" set aRcoefM(drams/kilogram) 564.38339119329 set aRexpM(drams/kilogram) 0 .fRright.fRmass.listboxM insert end "grains" set aRcoefM(grains/kilogram) 15432.358352941 set aRexpM(grains/kilogram) 0 .fRright.fRmass.listboxM insert end "grams" set aRcoefM(grams/kilogram) 1000.0 set aRexpM(grams/kilogram) 0 .fRright.fRmass.listboxM insert end "kilograms" set aRcoefM(kilograms/kilogram) 1.0 set aRexpM(kilograms/kilogram) 0 .fRright.fRmass.listboxM insert end "megagrams" set aRcoefM(megagrams/kilogram) 0.001 set aRexpM(megagrams/kilogram) 0 .fRright.fRmass.listboxM insert end "micrograms" set aRcoefM(micrograms/kilogram) 1000000000 set aRexpM(micrograms/kilogram) 0 .fRright.fRmass.listboxM insert end "milligrams" set aRcoefM(milligrams/kilogram) 1000000 set aRexpM(milligrams/kilogram) 0 .fRright.fRmass.listboxM insert end "ounces" set aRcoefM(ounces/kilogram) 35.27396194958 set aRexpM(ounces/kilogram) 0 .fRright.fRmass.listboxM insert end "pounds" set aRcoefM(pounds/kilogram) 2.2046226218488 set aRexpM(pounds/kilogram) 0 .fRright.fRmass.listboxM insert end "tons-metric" set aRcoefM(tons-metric/kilogram) 0.001 set aRexpM(tons-metric/kilogram) 0 .fRright.fRmass.listboxM insert end "tons-short-US" set aRcoefM(tons-US/kilogram) 0.0011023113109244 set aRexpM(tons-US/kilogram) 0 ##+###################################################### ## Put the units names in 'listboxV' (the volume listbox), ## and set the conversion factors --- aRcoefV and aRexpV --- ## for each volume unit --- how many cubic-meters in the ## volume unit. ## ## NOTE: 'cubicmeters' will be our 'base' unit of volume measure. ## (There are 1000 liters in a cubic meter.) ## ## Use about 10 significant digits for the coefficients. ## ## We put this list in alphabetical order by units name. ## Example: ## barrels ## cubic-centimeters ## cubic-feet ## cubic-inches ## cubic-kilometers ## cubic-lightyears ## cubic-meters ## cubic-miles ## cubic-yards ## cups ## deciliters ## gallons ## liters ## milliliters ## pints ## quarts ## tablespoons ## teaspoons ## ## ## 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: ## Cubes of length factors for converting a meter to ## a length in another distance unit yield volume factors that ## convert a cubic-meter into the cube of that unit. ## ## Other references specific to volume conversion: ## http://coolconversion.com/volume-unit-conversion ## http://www.onlineconversion.com/volume.htm ## http://www.thecalculatorsite.com/conversions/volume.php ## https://www.calculateme.com/Volume/ ## https://converterin.com/volume.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/volume-unit-conversion ##+###################################################### .fRright.fRvolume.listboxV insert end "barrel-petroleum-US" set aRcoefV(cubicmeters/barrel-petroleum-US) 0.158987295 set aRexpV(cubicmeters/barrel-petroleum-US) 0 .fRright.fRvolume.listboxV insert end "cubic-centimeter" set aRcoefV(cubicmeters/cubic-centimeter) 0.000001 set aRexpV(cubicmeters/cubic-centimeter) 0 .fRright.fRvolume.listboxV insert end "cubic-foot" set aRcoefV(cubicmeters/cubic-foot) 0.028316847 set aRexpV(cubicmeters/cubic-foot) 0 .fRright.fRvolume.listboxV insert end "cubic-inch" set aRcoefV(cubicmeters/cubic-inch) 0.000016387 set aRexpV(cubicmeters/cubic-inch) 0 .fRright.fRvolume.listboxV insert end "cubic-kilometer" set aRcoefV(cubicmeters/cubic-kilometer) 1.0 set aRexpV(cubicmeters/cubic-kilometer) 9 .fRright.fRvolume.listboxV insert end "cubic-lightyear" set aRcoefV(cubicmeters/cubic-lightyear) 0.846732408 set aRexpV(cubicmeters/cubic-lightyear) 48 .fRright.fRvolume.listboxV insert end "cubic-meter" set aRcoefV(cubicmeters/cubic-meter) 1.0 set aRexpV(cubicmeters/cubic-meter) 0 .fRright.fRvolume.listboxV insert end "cubic-mile" set aRcoefV(cubicmeters/cubic-mile) 0.416818183 set aRexpV(cubicmeters/cubic-mile) 10 .fRright.fRvolume.listboxV insert end "cubic-yard" set aRcoefV(cubicmeters/cubic-yard) 0.764554858 set aRexpV(cubicmeters/cubic-yard) 0 .fRright.fRvolume.listboxV insert end "cup-US" set aRcoefV(cubicmeters/cup-US) 0.000236588 set aRexpV(cubicmeters/cup-US) 0 .fRright.fRvolume.listboxV insert end "deciliter" set aRcoefV(cubicmeters/deciliter) 0.0001 set aRexpV(cubicmeters/deciliter) 0 .fRright.fRvolume.listboxV insert end "gallon-US-liquid" set aRcoefV(cubicmeters/gallon-US-liquid) 0.003785412 set aRexpV(cubicmeters/gallon-US-liquid) 0 .fRright.fRvolume.listboxV insert end "liter" set aRcoefV(cubicmeters/liter) 0.001 set aRexpV(cubicmeters/liter) 0 .fRright.fRvolume.listboxV insert end "milliliter" set aRcoefV(cubicmeters/milliliter) 0.000001 set aRexpV(cubicmeters/milliliter) 0 .fRright.fRvolume.listboxV insert end "ounce-US-liquid" set aRcoefV(cubicmeters/ounce-US-liquid) 0.000029574 set aRexpV(cubicmeters/ounce-US-liquid) 0 .fRright.fRvolume.listboxV insert end "pint-US-liquid" set aRcoefV(cubicmeters/pint-US-liquid) 0.000473176 set aRexpV(cubicmeters/pint-US-liquid) 0 .fRright.fRvolume.listboxV insert end "quart-US-liquid" set aRcoefV(cubicmeters/quart-US-liquid) 0.000946353 set aRexpV(cubicmeters/quart-US-liquid) 0 .fRright.fRvolume.listboxV insert end "tablespoon-US" set aRcoefV(cubicmeters/tablespoon-US) 0.000014787 set aRexpV(cubicmeters/tablespoon-US) 0 .fRright.fRvolume.listboxV insert end "teaspoon-US" set aRcoefV(cubicmeters/teaspoon-US) 0.000004929 set aRexpV(cubicmeters/teaspoon-US) 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 == 6} { ## If initial values are passed as ARGUMENTS to this script, they should be ## CHOSEunitsM1 CHOSEunitsV1 ENTRYcoef1 ENTRYexp1 CHOSEunitsM2 CHOSEunitsV2. ## Note: The units should be spelled exactly as units put in listboxes, above. set CHOSEunitsM1 [lindex $argv 0] set CHOSEunitsV1 [lindex $argv 1] set ENTRYcoef1 [lindex $argv 2] set ENTRYexp1 [lindex $argv 3] set CHOSEunitsM2 [lindex $argv 4] set CHOSEunitsV2 [lindex $argv 5] # density_update "2from1" } else { ## If 4 arguments have not been supplied, use DEFAULTS for ## CHOSEunitsM1 CHOSEunitsV1 ENTRYcoef1 ENTRYexp1 CHOSEunitsM2 CHOSEunitsV2. if {1} { ## A metric example: set CHOSEunitsM1 "kilograms" set CHOSEunitsV1 "cubic-centimeter" set ENTRYcoef1 "1.0" set ENTRYexp1 "0" set CHOSEunitsM2 "kilograms" set CHOSEunitsV2 "cubic-meter" } else { ## An English/US-units example: set CHOSEunitsM1 "pounds" set CHOSEunitsV1 "cubic-foot" set ENTRYcoef1 "1.0" set ENTRYexp1 "0" set CHOSEunitsM2 "pounds" set CHOSEunitsV2 "cubic-yard" } # density_update "2from1" } ##+######################################################################## ## Show the initial units1 and units2 on the GUI. ##+######################################################################## .fRleft.fRunits1.txtUnitsM1 delete 1.0 end .fRleft.fRunits1.txtUnitsM1 insert end "$CHOSEunitsM1" .fRleft.fRunits1.txtUnitsV1 delete 1.0 end .fRleft.fRunits1.txtUnitsV1 insert end "$CHOSEunitsV1" .fRleft.fRunits2.txtUnitsM2 delete 1.0 end .fRleft.fRunits2.txtUnitsM2 insert end "$CHOSEunitsM2" .fRleft.fRunits2.txtUnitsV2 delete 1.0 end .fRleft.fRunits2.txtUnitsV2 insert end "$CHOSEunitsV2" ##+#################################### ## 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 'density_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 "%15.15f" # 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 ** the weight-or-mass and/or volume listboxes to set a Unit of Measure for ** Density1 and/or Density2. Then ... Use a 'Calc' button to assure that ** the two versions of the density are equivalent."