#!/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: tkSpeedConvertSelect.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 SPEED ## in one type of units to another type of units. ## ## The GUI allows for showing a single speed in two ## different units of measure. ## ## (NOTE: ## We use the term SPEED rather than VELOCITY, because ## 'velocity' is a 'vector' and we are doing 'scalar' calculations. ## We imagine that we are dealing with 'linear' distances rather ## than 'curvilinear' distances and paths.) ## ## The GUI allows the user to pick a pair of speed units --- ## from units in two 'listbox' widgets. ## ## NOTE: ## Speed is a ratio of distance-over-time. If we imagine ## allowing for 10 different units for distance and 8 different ## units for time, this results in 10 x 8 = 80 different units ## of speed to choose from. ## ## Rather than using a single listbox with about 80 entries (or more), ## we use a 'distance' listbox and a 'time' listbox with about ## 10 to 20 entries each. ## ## Examples of distance units: inches, feet, yards, miles, ## kilometers, meters, centimeters, millimeters, ## micrometers, nanometers, light-years, etc. ## ## Examples of time units (alphabetically): century, day, decade, ## hour, microsecond, millisecond, minute, second, week, year. ## ## This converter is intended to be able to convert speeds chosen ## from a huge range of sizes: speeds on the order of a ## a lightyear-per-year (the speed of light ; the theoretical ## maximum speed of any entity) --- or faster --- to extremely slow ## speeds, on the order of a micrometer-per-century or slower. ## ## 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. Imagine traversing the diameter of the ## 'observable' universe in a second. Even though this is theoretically ## impossible, this GUI allows for dealing with speeds like ## 10 to the 27th meters per second. ## ## Similarly for extremely small speeds. ## ## 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 speeds encountered by traversing the ## diameter of a subatomic particle in a century (~3 times 10^9 seconds) ## or more. ## ## Traversing 10^-14 meters in 10^9 seconds is a speed on the order of ## 10^-23 meters per second. ## This GUI is meant to handle such extremely slow speeds. ## ## In order to be able to handle such huge ranges, in the various ## units, the GUI allows for specifying the speed (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 speed value --- by clicking on a 'UseIt' button. ## Both forms of the speed, 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 SPEED CONVERTER-SELECTOR utility. ## ##+################# ## THE GUI WIDGETS: ## ## The options available to the user are indicated by ## the following 'sketch' of the GUI: ## ## FRAMEnames ## VVVVVVVVVV ## ----------------------------------------------------------------------------------- ## tkSpeed - 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 --- '.fRdist' and ## .fRtime --- in this '.fRright' frame.] ## ## |----------------A |----------------A ## .fRleft.fRunits1 Units1:[selected units displayed here] O units1 | centimeters | | century | ## [in 2 text widgets] | feet | | day | ## .fRleft.fRvalues1 Coefficient: ___________ Exponent: {+}___{-} | inches | | decade | ## | ... | | ... | ## .fRleft.fRunits2 Units2:[selected units displayed here] O units2 | kilometers | | hour | ## [in 2 text widgets] | lightyears | | ... | ## .fRleft.fRvalues2 Coefficient: ____________ Exponent: {+}___{-} | meters | | ... | ## | miles | | minute | ## | ... | | ... | ## | nanometers | | second | ## | ... | | ... | ## | yards | | year | ## |<-------------->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 speed 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 speed in the two user-selected units. ## ## Sample output string: ## inches second 6.000000 1 feet second 5.000000 -1 ## ##+##################################################################### ## CALL FORMAT: ## ## .../tkSpeedConvertSelect.tk [unitsD1 unitsT1 coef1 exp1 unitsD2 unitsT2] ## ## 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/tkSpeedConvertSelect.tk` ## or ## TEMP=`$FEDIR_TKGUIS/tkSpeedConvertSelect.tk inches second 6.0 1 centimeters second` ## ## (The coef2 and exp2 values are computed from coef1 and exp1 --- ## according to the pair unitsD1,unitsT1 and the pair unitsD2,unitsT2.) ## ## OR if we ever implement initializing the GUI via environment variables: ## ## 2) COEF1=6.0 ## EXP1=1 ## UNITSD1="inches" ## UNITST1="seconds" ## UNITSD2="centimeters" ## UNITST2="seconds" ## export COEF1 EXP1 UNITSD1 UNITST1 UNITSD2 UNITST2 ## ## TEMP=`$FEDIR_TKGUIS/tkSpeedConvertSelect.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. ## ## - 'speed_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 'speed_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 2017aug30 Started version for the FE 'tkGooies' ## system, on Linux. ## Changed by: Blaise Montandon 2017sep01 Changed 'FORMATforCOEF' to support ## 15 decimal places instead of 8. ##+############################################################################ ##+################################# ## SET THE TOP WINDOW NAME. ##+################################# wm title . "tkSpeed - Converter / Selector" wm iconname . "SpeedConvert" # 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 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(labelUnitsD1) "Units1:" set aRtext(labelUnitsT1) " per " set aRtext(labelUnitsD2) "Units2:" set aRtext(labelUnitsT2) " 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.fRdist' - to contain a listbox widget with x,y scrollbars. ## '.fRright.fRtime' - 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.fRdist -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRright.fRtime -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.fRdist \ .fRright.fRtime \ -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 {speed_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 {speed_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_forUnitsD 6 set TextWidthChars_forUnitsD 14 set LabelWidthChars_forUnitsT 4 set TextWidthChars_forUnitsT 14 set EntryWidthChars_forCoef 16 set EntryWidthChars_forExp 4 ##+######################################################## ## IN THE '.fRleft.fRunits1' frame -- ## DEFINE 2 pairs of LABEL and TEXT widgets (to display ## distance and time units) and 1 RADIOBUTTON. ## THEN PACK THEM. ##+######################################################## label .fRleft.fRunits1.labelUnitsD1 \ -text "$aRtext(labelUnitsD1)" \ -font fontTEMP_label \ -width $LabelWidthChars_forUnitsD \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits1.txtUnitsD1 \ -height 1 \ -width $TextWidthChars_forUnitsD \ -font fontTEMP_text \ -wrap none \ -borderwidth $BDwidthPx_text \ -relief $RELIEF_text \ -bg $textBKGD label .fRleft.fRunits1.labelUnitsT1 \ -text "$aRtext(labelUnitsT1)" \ -font fontTEMP_label \ -width $LabelWidthChars_forUnitsT \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits1.txtUnitsT1 \ -height 1 \ -width $TextWidthChars_forUnitsT \ -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.labelUnitsD1 \ .fRleft.fRunits1.txtUnitsD1 \ .fRleft.fRunits1.labelUnitsT1 \ .fRleft.fRunits1.txtUnitsT1 \ -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 ## distance and time units) and 1 RADIOBUTTON. ## THEN PACK THEM. ##+######################################################## label .fRleft.fRunits2.labelUnitsD2 \ -text "$aRtext(labelUnitsD2)" \ -width $LabelWidthChars_forUnitsD \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits2.txtUnitsD2 \ -height 1 \ -width $TextWidthChars_forUnitsD \ -font fontTEMP_text \ -wrap none \ -borderwidth $BDwidthPx_text \ -relief $RELIEF_text \ -bg $textBKGD label .fRleft.fRunits2.labelUnitsT2 \ -text "$aRtext(labelUnitsT2)" \ -width $LabelWidthChars_forUnitsT \ -font fontTEMP_label \ -justify left \ -anchor w \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label text .fRleft.fRunits2.txtUnitsT2 \ -height 1 \ -width $TextWidthChars_forUnitsT \ -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.labelUnitsD2 \ .fRleft.fRunits2.txtUnitsD2 \ .fRleft.fRunits2.labelUnitsT2 \ .fRleft.fRunits2.txtUnitsT2 \ -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.fRdist' frame - ## DEFINE-and-PACK 1 LISTBOX widget (with x,y scrollbars). ##+###################################################### listbox .fRright.fRdist.listboxD \ -width $initListboxWidthChars \ -height $initListboxHeightChars \ -font fontTEMP_listbox \ -borderwidth $BDwidthPx_listbox \ -relief $RELIEF_listbox \ -state normal \ -yscrollcommand ".fRright.fRdist.scrbary set" \ -xscrollcommand ".fRright.fRdist.scrbarx set" scrollbar .fRright.fRdist.scrbary \ -orient vertical -command ".fRright.fRdist.listboxD yview" scrollbar .fRright.fRdist.scrbarx \ -orient horizontal -command ".fRright.fRdist.listboxD xview" ## Pack the widgets in frame '.fRright.fRdist'. pack .fRright.fRdist.scrbary \ -side right \ -anchor e \ -fill y \ -expand 0 pack .fRright.fRdist.scrbarx \ -side bottom \ -anchor sw \ -fill x \ -expand 0 pack .fRright.fRdist.listboxD \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+###################################################### ## In the '.fRright.fRtime' frame - ## DEFINE-and-PACK 1 LISTBOX widget (with x,y scrollbars). ##+###################################################### listbox .fRright.fRtime.listboxT \ -width $initListboxWidthChars \ -height $initListboxHeightChars \ -font fontTEMP_listbox \ -borderwidth $BDwidthPx_listbox \ -relief $RELIEF_listbox \ -state normal \ -yscrollcommand ".fRright.fRtime.scrbary set" \ -xscrollcommand ".fRright.fRtime.scrbarx set" scrollbar .fRright.fRtime.scrbary \ -orient vertical -command ".fRright.fRtime.listboxT yview" scrollbar .fRright.fRtime.scrbarx \ -orient horizontal -command ".fRright.fRtime.listboxT xview" ## Pack the widgets in frame '.fRright.fRtime'. pack .fRright.fRtime.scrbary \ -side right \ -anchor e \ -fill y \ -expand 0 pack .fRright.fRtime.scrbarx \ -side bottom \ -anchor sw \ -fill x \ -expand 0 pack .fRright.fRtime.listboxT \ -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.fRdist.listboxD "listboxD_select_units" bind .fRright.fRtime.listboxT "listboxT_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 speeds 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 speeds 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 speeds 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 speeds may no longer be equivalent. set ENTRYcoef1 "" set ENTRYexp1 "0" } ##+##################################################################### ##+##################################################################### ## DEFINE PROCEDURES: ## ## 'listboxD_select_units' - called by a button1-release binding on ## the 'listboxD' widget. ## ## 'listboxT_select_units' - called by a button1-release binding on ## the 'listboxT' widget. ## ## 'speed_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 'speed_update' proc. ## 'decimal_check' - called by the 'edit_inputs' proc. ## ## 'normalize_coef_exp' - called by the 'speed_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 'listboxD_select_units' ##+################################################################ ## PURPOSE: Sets some DISTANCE 'units' variables depending on the ## current setting of the radiobutton variable RADVARunits1or2. ## ## CALLED BY: binding on the 'listboxD' widget ##+################################################################ proc listboxD_select_units {} { global RADVARunits1or2 CHOSEunitsD1 CHOSEunitsD2 global ENTRYcoef1 ENTRYexp1 ENTRYcoef2 ENTRYexp2 set sel_index [ .fRright.fRdist.listboxD curselection ] if { $sel_index != "" } { set selectedUNITS [ .fRright.fRdist.listboxD get $sel_index ] } ## FOR TESTING: # puts "listboxD_select_units: selectedUNITS = $selectedUNITS" ######################################################## ## Load in the units name in a CHOSEunits variable and in ## a text widget. ######################################################## if {"$RADVARunits1or2" == "units1"} { set CHOSEunitsD1 "$selectedUNITS" .fRleft.fRunits1.txtUnitsD1 delete 1.0 end .fRleft.fRunits1.txtUnitsD1 insert end "$CHOSEunitsD1" ## 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 CHOSEunitsD2 "$selectedUNITS" .fRleft.fRunits2.txtUnitsD2 delete 1.0 end .fRleft.fRunits2.txtUnitsD2 insert end "$CHOSEunitsD2" ## 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 'listboxD_select_units' ##+################################################################ ## PROC 'listboxT_select_units' ##+################################################################ ## PURPOSE: Sets some TIMEE 'units' variables depending on the ## current setting of the radiobutton variable RADVARunits1or2. ## ## CALLED BY: binding on the 'listboxD' widget ##+################################################################ proc listboxT_select_units {} { global RADVARunits1or2 CHOSEunitsT1 CHOSEunitsT2 global ENTRYcoef1 ENTRYexp1 ENTRYcoef2 ENTRYexp2 set sel_index [ .fRright.fRtime.listboxT curselection ] if { $sel_index != "" } { set selectedUNITS [ .fRright.fRtime.listboxT get $sel_index ] } ## FOR TESTING: # puts "listboxT_select_units: selectedUNITS = $selectedUNITS" ######################################################## ## Load in the units name in a CHOSEunits variable and ## in a text widget. ######################################################## if {"$RADVARunits1or2" == "units1"} { set CHOSEunitsT1 "$selectedUNITS" .fRleft.fRunits1.txtUnitsT1 delete 1.0 end .fRleft.fRunits1.txtUnitsT1 insert end "$CHOSEunitsT1" ## 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 CHOSEunitsT2 "$selectedUNITS" .fRleft.fRunits2.txtUnitsT2 delete 1.0 end .fRleft.fRunits2.txtUnitsT2 insert end "$CHOSEunitsT2" ## 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 'listboxT_select_units' ##+##################################################################### ## PROC: 'speed_update' ##+##################################################################### ## ## PURPOSE: If the argument to this proc is the string "2from1", ## use CHOSEunitsD1 CHOSEunitsT1 ENTRYcoef1 ENTRYexp1 and ## CHOSEunitsD2 CHOSEunitsT2 ## to compute ENTRYcoef2 and ENTRYexp2. ## AND ## If the argument to this proc is the string "1from2", ## use CHOSEunitsD2 CHOSEunitsT2 ENTRYcoef2 ENTRYexp2 and ## CHOSEunitsD1 CHOSEunitsT1 ## 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 speed_update {CONVERTtype} { ## Dummy out this proc, for testing. # return ######################################################################## ## The variable 'CONVERTtype' should be the string "2from1" or "1from2". ######################################################################## global CHOSEunitsD1 CHOSEunitsT1 ENTRYcoef1 ENTRYexp1 \ CHOSEunitsD2 CHOSEunitsT2 ENTRYcoef2 ENTRYexp2 \ aRcoefD aRexpD aRcoefT aRexpT 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 \$aRcoefD($CHOSEunitsD1/meter) eval set ExpDunits1PerDstd \$aRexpD($CHOSEunitsD1/meter) eval set CoefTstdPerTunits1 \$aRcoefT(seconds/$CHOSEunitsT1) eval set ExpTstdPerTunits1 \$aRexpT(seconds/$CHOSEunitsT1) eval set CoefDunits2PerDstd \$aRcoefD($CHOSEunitsD2/meter) eval set ExpDunits2PerDstd \$aRexpD($CHOSEunitsD2/meter) eval set CoefTstdPerTunits2 \$aRcoefT(seconds/$CHOSEunitsT2) eval set ExpTstdPerTunits2 \$aRexpT(seconds/$CHOSEunitsT2) ################################################################### ## If CONVERTtype = '2from1', use ## ENTRYcoef1 ENTRYexp1 and the pair CHOSEunitsD1,CHOSEunitsT1 and ## the pair CHOSEunitsD2,CHOSEunitsT2 to calculate ## ENTRYcoef2 and ENTRYexp2. ## ## Note that ENTRYcoef1 is speed in unitsD1/unitsT1. ## To convert ENTRYcoef1 to unitsD2/unitsT2 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 unitsD2/unitsT2 by ## multiplying by CoefDunits2PerDstd and multiplying by CoefTstdPerTunits2 ## --- i.e multiply by (CoefDunits2PerDstd * CoefTstdPerTunits2). ## ## To convert the exponent ENTRYexp1 to unitsD2/unitsT2 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 unitsD2/unitsT2 ## 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 CHOSEunitsD2,CHOSEunitsT2 and ## the pair CHOSEunitsD1,CHOSEunitsT1 to calculate ## ENTRYcoef1 and ENTRYexp1. ## ## Note that ENTRYcoef2 is speed in unitsD2/unitsT2. ## To convert ENTRYcoef2 to unitsD1/unitsT1 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 unitsD1/unitsT1 by ## multiplying by CoefDunits1PerDstd and multiplying by CoefTstdPerTunits1 ## --- i.e. multiply by (CoefDunits1PerDstd * CoefTstdPerTunits1). ## ## To convert the exponent ENTRYexp2 to unitsD1/unitsT1 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 unitsD1/unitsT1 ## 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 'speed_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 'speed_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 speed $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYexp1" == ""} { popup_msgVarWithScroll .topErr "Exponent for UNITS1 speed $MSGblank" +10+10 set EDITcode 1 return } ########################################################## ## Check that ENTRYexp1 is integer. ########################################################## if {![string is integer -strict "$ENTRYexp1"]} { popup_msgVarWithScroll .topErr "Exponent for speed 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 speed $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 speed $MSGblank" +10+10 set EDITcode 1 return } if {"$ENTRYexp2" == ""} { popup_msgVarWithScroll .topErr "Exponent for UNITS2 speed $MSGblank" +10+10 set EDITcode 1 return } ########################################################## ## Check that ENTRYexp2 is integer. ########################################################## if {![string is integer -strict "$ENTRYexp2"]} { popup_msgVarWithScroll .topErr "Exponent for speed 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 speed $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 CHOSEunitsD1 CHOSEunitsT1 ENTRYcoef1 ENTRYexp1 \ CHOSEunitsD2 CHOSEunitsT2 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 \ "$CHOSEunitsD1 $CHOSEunitsT1 $ENTRYcoef1 $ENTRYexp1 \ $CHOSEunitsD2 $CHOSEunitsT2 $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 ## 'speed_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 \ " ************ tkSpeed - Converter / Selector Utility ********* This Tk GUI script provides a GUI for CONVERTING a SPEED in one unit of measure to the speed in a second unit of measure. The speeds 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 speeds over a huge range of magnitudes --- from a 'nanometer/century' (or less) to a 'lightyear/microsecond' (or more) --- i.e. more than the speed-of-light! 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. ********************************************* HOW SPEED 'units1' and 'units2' ARE SPECIFIED on this GUI ********************************************* Speed is a ratio of distance-over-time. If we imagine allowing for 10 different units-of-measure for distance and 8 different units-of-measure for time, this results in 10 x 8 = 80 different units-of-measure of speed to choose from. Rather than using a single listbox with about 80 entries (or more), we use a 'distance' listbox and a 'time' listbox with about 10 to 20 entries each. Examples of distance units: inches, feet, yards, miles, kilometers, meters, centimeters, millimeters, micrometers, nanometers, light-years, etc. Examples of time units (alphabetically): century, day, decade, hour, microsecond, millisecond, minute, second, week, year, etc. ******************* 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 'distance' listbox and in the 'time' listbox. STEP 2: (calculating one speed 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. ****************** SOME SPECIAL CASES (knots, speed-of-light, speed-of-sound) ****************** Since this GUI uses two units-listboxes (for distance and time) rather than one listbox (for speed units), the question arises 'How do I convert knots to other speed units?'. On this GUI, one can simply choose 'nautical-miles' for the distance units and 'hours' for the time units --- in the 'units1' line or the 'units2' line of the GUI --- because a 'knot' is a 'nautical-mile' per 'hour'. --- Another question: 'How do I convert the speed-of-light (or a fraction of the speed-of-light) to other speed units?'. On this GUI, one can simply choose 'lightyears' for the distance units and 'years' for the time units. Essentially, a 'lightyear' is 'speed-of-light' times one 'year' --- and dividing by a year gives a 'speed-of-light' unit --- in algebra-class parlance 'the years cancel'. (NOTE: This GUI does not restrict you to speeds less than the speed-of-light. You can specify speeds much larger than the speed of light. For example, if you have set the distance units to 'lightyears' and time to 'years', for 'units1', you can enter numbers larger than 1.0 and 0 in the 'units1' entry fields for coefficient and exponent.) --- Another question: 'How do I convert the speed-of-sound (or a fraction or multiple of the speed-of-sound) --- in other words 'mach' units --- to other speed units?'. On this GUI, one can simply choose 'mach-seconds' for the distance units and 'seconds' for the time units. One mach-second is the distance that sound travels in a second. (NOTE: The distance that sound travels, through air, in one second is about 343 meters --- Ref: https://en.wikipedia.org/wiki/Speed_of_sound --- assuming atmospheric conditions similar to average conditions near sea-level on Earth. So 1 mach-second divided by 343 meters gives a 'mach-seconds/meter' conversion ratio of about 1 / 343 = 0.0029154 . That ratio is used by this utility.) ******************************* SELECTION AS WELL AS CONVERSION: ******************************* In addition to CONVERTING speeds, this GUI can also be used for SELECTING the speed --- expressed as both the Units1 coefficient and exponent AND the Units2 coefficient and exponent. When the user clicks on the 'UseIt' button, the speed is written as a text string to 'stdout' in both forms --- as two 'quadlets'. Sample output: meters second 1.000000 3 kilometers second 1.000000 0 Thus this utility Tk script can be called from within a shell script or from within a Tcl-Tk script to get a speed. *********************************************** 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/tkSpeedConvertSelect.tk` OR in the form TEMP=`\$SCRIPT_LOC/tkSpeedConvertSelect.tk unitsD1 unitsT1 coef1 exp1 unitsD2 unitsT2` with specific values such as TEMP=`\$SCRIPT_LOC/tkSpeedConvertSelect.tk inches hour 6.0 1 centimeters second` The coef2 and exp2 values can then be computed from units1, coef1, exp1, and units2 by a click on a 'Calc' button --- and these six values 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 SPEED CONVERTER-SELECTOR by calculating and outputing a currently specified speed to 'stdout' (standard output), when the 'UseIt' button is clicked. Example output string format: unitsD1 unitsT1 coeff1 exp1 unitsD2 unitsT2 coeff2 exp2 Specific example: meters second 1.000000 3 kilometers second 1.000000 0 " ##+############################################### ##+############################################### ## ADDITONAL GUI INITIALIZATION section FOLLOWS. ##+############################################### ##+############################################### ##+###################################################### ## Put the units names in 'listboxD' (the distance listbox), ## and set the conversion factors --- aRcoefD and aRexpD --- ## for each distance unit --- how many units in a 'meter'. ## ## NOTE: 'meter' will be our 'base' unit of distance measure. ## ## Use about 10 significant digits for the coefficients. ## ## We put this list in alphabetical order by units name. ## Example: ## centimeters ## feet ## inches ## kilometers ## lightyears ## meters ## miles ## yards ## ## Some references for distance/length conversion: ## http://coolconversion.com/length-distance-full-unit-conversion ## http://www.rapidtables.com/convert/length/index.htm ## http://www.onlineconversion.com/length_all.htm ## http://www.thecalculatorsite.com/conversions/lengthanddistance.php ## https://www.calculateme.com/Length/ ## https://converterin.com/length-distance.html ## https://www.translatorscafe.com/unit-converter/en/length/ ## ## 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 distance conversion factors below were found ## using http://coolconversion.com/length-distance-full-unit-conversion ##+###################################################### ## To test the speed conversion of this GUI, here are ## some references for speed (or velocity) conversion: ## http://www.onlineconversion.com/speed.htm ## http://www.onlineconversion.com/speed_common.htm ## http://www.onlineconversion.com/speed_all.htm ## http://coolconversion.com/speed-unit-conversion ## http://www.thecalculatorsite.com/conversions/velocity.php ## https://www.calculateme.com/Speed/ ## https://converterin.com/speed.html ## ##+###################################################### .fRright.fRdist.listboxD insert end "centimeters" set aRcoefD(centimeters/meter) 100.0 set aRexpD(centimeters/meter) 0 .fRright.fRdist.listboxD insert end "feet" set aRcoefD(feet/meter) 3.280839895 set aRexpD(feet/meter) 0 .fRright.fRdist.listboxD insert end "inches" set aRcoefD(inches/meter) 39.37007874 set aRexpD(inches/meter) 0 .fRright.fRdist.listboxD insert end "kilometers" set aRcoefD(kilometers/meter) 0.001 set aRexpD(kilometers/meter) 0 .fRright.fRdist.listboxD insert end "lightyears" set aRcoefD(lightyears/meter) 1.0570008340246 set aRexpD(lightyears/meter) -16 .fRright.fRdist.listboxD insert end "meters" set aRcoefD(meters/meter) 1.0 set aRexpD(meters/meter) 0 .fRright.fRdist.listboxD insert end "mach-seconds" ## One mach-second is the distance sound travels in a second. ## The value is about 343 meters (from Wikipedia) --- assuming atmospheric ## conditions similar to average conditions near sea-level on Earth. ## So 1 mach-second divided by 343 meters gives the ratio : set aRcoefD(mach-seconds/meter) 0.002915452 set aRexpD(mach-seconds/meter) 0 .fRright.fRdist.listboxD insert end "micrometers" set aRcoefD(micrometers/meter) 1.0 set aRexpD(micrometers/meter) 6 .fRright.fRdist.listboxD insert end "miles" set aRcoefD(miles/meter) 0.0006213712 set aRexpD(miles/meter) 0 .fRright.fRdist.listboxD insert end "nanometers" set aRcoefD(nanometers/meter) 1.0 set aRexpD(nanometers/meter) 9 .fRright.fRdist.listboxD insert end "nautical-miles" ## FROM https://converterin.com/length-distance/meter-m-to-nautical-miles.html # set aRcoefD(nautical-miles/meter) 0.00017998560115 WRONG? ## FROM https://www.translatorscafe.com/unit-converter/en/length/ ## AND https://www.checkyourmath.com/convert/length/m_nautical_miles.php set aRcoefD(nautical-miles/meter) 0.0005399568034557 set aRexpD(nautical-miles/meter) 0 .fRright.fRdist.listboxD insert end "yards" set aRcoefD(yards/meter) 1.093613298 set aRexpD(yards/meter) 0 ##+###################################################### ## Put the units names in 'listboxT' (the time listbox), ## and set the conversion factors --- aRcoefT and aRexpT --- ## for each distance unit --- how many units in a 'second'. ## ## NOTE: 'second' will be our 'base' unit of time measure. ## ## Use about 10 significant digits for the coefficients. ## ## We put this list in alphabetical order by units name. ## Example: ## day ## decade ## gigayear [billion years] ## hour ## megayear [million years] ## microsecond ## millisecond ## minute ## month-30-day ## nanosecond [billionth of a second] ## second ## week ## year ## ## day-in-secs = 24 hrs/day * 60 min/hr * 60 secs/min = 86400 secs ## month-30-day-in-secs = 30 * 86400 = 2592000 secs ## week-in-secs = 7 * day-in-secs = 7 * 86400 = 604800 secs ## year-in-secs = 52 * week-in-secs = 52 * 604800 = 31449600 secs ## decade-in-secs = 10 * year-in-secs = 10 * 31449600 = 314496 * 10^3 secs ## century-in-secs = 10 * decade-in-secs = 10 * 314496 * 10^3 = 314496 * 10^4 secs ## megayear = 31449600 * 10^6 secs ## gigayear = 31449600 * 10^9 secs ## millisec = 10^-3 secs ## microsec = 10^-6 secs ## nanosec = 10^-9 secs ## ## NOTE: The conversion factors below may not be to the ## precision required to answer certain questions. ## In fact, there may be some gross errors in one or ## more of these conversion factors. Testing should be ## done to determine the accuracy of these factors and ## adjust them as needed. ##+###################################################### .fRright.fRtime.listboxT insert end "century" set aRcoefT(seconds/century) 314496 set aRexpT(seconds/century) 4 .fRright.fRtime.listboxT insert end "day" set aRcoefT(seconds/day) 86400 set aRexpT(seconds/day) 0 .fRright.fRtime.listboxT insert end "decade" set aRcoefT(seconds/decade) 314496 set aRexpT(seconds/decade) 3 .fRright.fRtime.listboxT insert end "gigayear" set aRcoefT(seconds/gigayear) 31449600 set aRexpT(seconds/gigayear) 9 .fRright.fRtime.listboxT insert end "hour" set aRcoefT(seconds/hour) 3600 set aRexpT(seconds/hour) 0 .fRright.fRtime.listboxT insert end "megayear" set aRcoefT(seconds/megayear) 31449600 set aRexpT(seconds/megayear) 6 .fRright.fRtime.listboxT insert end "microsecond" set aRcoefT(seconds/microsecond) 1.0 set aRexpT(seconds/microsecond) -6 .fRright.fRtime.listboxT insert end "millisecond" set aRcoefT(seconds/millisecond) 1.0 set aRexpT(seconds/millisecond) -3 .fRright.fRtime.listboxT insert end "minute" set aRcoefT(seconds/minute) 60 set aRexpT(seconds/minute) 0 .fRright.fRtime.listboxT insert end "month-30-day" set aRcoefT(seconds/month-30-day) 2592 set aRexpT(seconds/month-30-day) 3 .fRright.fRtime.listboxT insert end "nanosecond" set aRcoefT(seconds/nanosecond) 1.0 set aRexpT(seconds/nanosecond) -9 .fRright.fRtime.listboxT insert end "second" set aRcoefT(seconds/second) 1.0 set aRexpT(seconds/second) 0 .fRright.fRtime.listboxT insert end "week" set aRcoefT(seconds/week) 604800 set aRexpT(seconds/week) 0 .fRright.fRtime.listboxT insert end "year" set aRcoefT(seconds/year) 31449600 set aRexpT(seconds/year) 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 ## CHOSEunitsD1 CHOSEunitsT1 ENTRYcoef1 ENTRYexp1 CHOSEunitsD2 CHOSEunitsT2. ## Note: The units should be spelled exactly as units put in listboxes, above. set CHOSEunitsD1 [lindex $argv 0] set CHOSEunitsT1 [lindex $argv 1] set ENTRYcoef1 [lindex $argv 2] set ENTRYexp1 [lindex $argv 3] set CHOSEunitsD2 [lindex $argv 4] set CHOSEunitsT2 [lindex $argv 5] # speed_update "2from1" } else { ## If 4 arguments have not been supplied, use DEFAULTS for ## CHOSEunitsD1 CHOSEunitsT1 ENTRYcoef1 ENTRYexp1 CHOSEunitsD2 CHOSEunitsT2. set CHOSEunitsD1 "miles" set CHOSEunitsT1 "hour" set ENTRYcoef1 "100.0" set ENTRYexp1 "0" set CHOSEunitsD2 "kilometers" set CHOSEunitsT2 "hour" # speed_update "2from1" } ##+######################################################################## ## Show the initial units1 and units2 on the GUI. ##+######################################################################## .fRleft.fRunits1.txtUnitsD1 delete 1.0 end .fRleft.fRunits1.txtUnitsD1 insert end "$CHOSEunitsD1" .fRleft.fRunits1.txtUnitsT1 delete 1.0 end .fRleft.fRunits1.txtUnitsT1 insert end "$CHOSEunitsT1" .fRleft.fRunits2.txtUnitsD2 delete 1.0 end .fRleft.fRunits2.txtUnitsD2 insert end "$CHOSEunitsD2" .fRleft.fRunits2.txtUnitsT2 delete 1.0 end .fRleft.fRunits2.txtUnitsT2 insert end "$CHOSEunitsT2" ##+#################################### ## 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 'speed_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 distance and time listboxes to set a Unit of Measure for Speed1 ** and/or Speed2. Then ... Use a 'Calc' button to assure that the two ** versions of the speed are equivalent."