#!/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/bin/local/wish /usr/bin/wish ## The form of this command: ## ln -s ##+####################################################################### ## Tk SCRIPT NAME: tkDegreesMinSecConvertSelect.tk ## ## WHERE: in $FEDIR_TKGUIS/SELECTORtools = $FEDIR/tkGUIs/SELECTORtools ## ## where $FEDIR is the installation ## directory of an FE subsystem. ## Ref: www.freedomenv.com ##+####################################################################### ## PURPOSE: This TkGUI script provides a GUI for CONVERTING ## between an angle in ## - decimal-degrees form (Example: 60.000) ## and ## - degrees,minutes,seconds form (Example: 60 0 0). ## ## This GUI is also for SELECTING the chosen degrees value ## by clicking on a 'UseIt' button. Both forms of the ## degrees value (decimal and degrees-minutes-seconds) are ## sent to 'stdout'. ## ## METHOD: There are 4 'scale' widgets on the GUI: ## - one for decimal degrees (range: 0.000 up to 360.000) ## and ## - three (in integers) for ## - degrees (range: 0 up to 360) ## - minutes (range: 0 up to 60) ## - seconds (range: 0 up to 60 -- or 0.00 to 60.00) ## ## To give some visual feedback of the angle size: ## For any slider bar change, this GUI script ## shows the angle depicted in a 'canvas' widget ## on which a circle is drawn and the size of the angle ## is shown via a color-filled sector in the circle. ## ## The canvas widget is on one side of the GUI ## window (covering about 20% of the window). ## ## Also the GUI shows the current angle's decimal and ## degrees-minutes-seconds values in two small text widgets ## about 12 characters long. ## ## This GUI also accepts an angle (in decimal form or in ## degrees-minutes-seconds form) to initialize the 4 scale widgets. ## ## SOME USES: ## ## 1) Useful for CONVERTING an angle in degrees-minutes-seconds form ## to decimal form. ## ## 2) IN A SHELL SCRIPT OR ANOTHER TK SCRIPT, this Tk script can ## ACT AS AN ANGLE CONVERTER-SELECTOR by passing a currently ## selected angle (in both forms) to stdout, ## when the OK/UseIt button is clicked. ## Example output string: 60.000 60 0 0 ## ##+################# ## THE GUI WIDGETS: ## ## The options available to the user are indicated by ## the following 'sketch' of the GUI: ## ## FRAMEnames ## VVVVVVVVVV ## ------------------------------------------------------------------------------------------ ## tkDegreesMinSec - Converter/Selector ## [window title] ## ------------------------------------------------------------------------------------------ ## ## .fRbuttons {UseIt} {Cancel} {Help} Decimal-Degrees: 60.000 Degrees-Minutes-Seconds: 60 0 0 ## ## .fRleft .fRright [the canvas is in this frame] ## ## .fRleft.fRsliders1 Decimal 0.000 360.000 |-------------------------------------| ## Degrees: <--------------O-----------> | | ## | canvas to hold circle depicting | ## .fRleft.fRsliders3 Integer 0 360 | | ## Degrees: <--------------O-----------> | the current angle as a sector in | ## | | ## Integer 0 60 | the circle | ## Minutes: <--------------O-----------> | | ## | | ## Integer 0 60 | | ## Seconds: <--------------O-----------> |-------------------------------------| ## ## ------------------------------------------------------------------- ## ## 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. ## <----O----> indicates a Tk 'scale' widget. ## UNDERSCORES indicate a Tk 'entry' widget. ## CAPITAL-O indicates a Tk 'radiobutton' widget. ## CAPITAL-X indicates a Tk 'checkbutton' widget (if any). ## ##+############## ## GUI components: ## ## From the GUI 'sketch' above, it is seen that the GUI consists of ## about ## ## - 3 button widgets ## - 2 (or 4) label widgets ## - 4 scale widgets ## - 0 entry widgets ## - 0 radiobutton widgets ## - 0 checkbutton widgets ## - 0 listbox widgets ## ##+##################################################################### ## CALLED BY: a 'SELECTORtools' toolchest in the 'tkGooies' FE system ## OR ## in a shell script or another Tk script. ##+##################################################################### ## INPUTS: The user slides the slider bars. Initial degree vals can be set ## as described in the next section, 'CALL FORMAT'. ## ## OUTPUT: The decimal-degrees and degrees-minutes-seconds values displayed ## on the Tk window. ## ## Also the last slider settings are passed to stdout ## as a string giving decimal-degrees and degrees-minutes-seconds ## values. ## ## Sample output string: 60.000 60 0 0 ## ##+##################################################################### ## CALL FORMAT: ## ## .../tkDegreesMinSecConvertSelect.tk [decimal-degrees] ## or ## .../tkDegreesMinSecConvertSelect.tk [degrees minutes seconds] ## ## OR ## ## use env vars to initialize the sliders. ## ----------------------------------------------------------------------- ## EXAMPLE CALLS in a shell script: ## (Could also be called in a tcl-tk script.) ## ## 1) ## TEMP=`$FEDIR_TKGUIS/tkDegreesMinSecConvertSelect.tk 60 0 0` ## or ## TEMP=`$FEDIR_TKGUIS/tkDegreesMinSecConvertSelect.tk` ## OR ## 2) DEG360=60 ## MIN60=0 ## SEC60=0 ## export DEG360 MIN60 SEC60 ## ## TEMP=`$FEDIR_TKGUIS/tkDegreesMinSecConvertSelect.tk` ## ## ## (Note: The values in TEMP can be extracted with a command like ## 'cut' or 'awk'.) ##+######################################################################## ## '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' , '.fRleft' , '.fRright' ## ## Sub-frames: '.fRleft.fRsliders1' , '.fRleft.fRsliders3' ## ## Sub-Sub-frames: '.fRleft.fRsliders3.fRdeg' , ## '.fRleft.fRsliders3.fRmin' , ## '.fRleft.fRsliders3.fRsec' ## ## '.fRbuttons' is to be on the top of the GUI, and ## '.fRleft' , '.fRright' are to be under '.fRbuttons' ## 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 and label & text widgets ## to display the current angle, in both forms.) ## ## - '.fRleft.fRsliders1' (to contain 1 sliderbar) ## - '.fRleft.fRsliders3' (to contain 3 sliderbars) ## ## - 'fRright' (to contain a canvas widget) ## ## 3) Define BINDINGS: none currently ## ## 4) Define PROCS: ## - 'angle_update' - for '-command' options on 4 scale widgets ## - 'draw_circle' ## - 'draw_angle' ## - 'put_vars' - for UseIt button ## - 'popup_msg_var_scroll' - for Help button ## ## 5) Additional GUI INITIALIZATION: call 'draw_circle' --- and set ## the HELPtext var for the Help button. ## Also set an initial angle. ##+####################################################################### ## 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 2016mar10 Started version for the FE 'tkGooies' ## system, on Linux. ## Changed by: Blaise Montandon 2016 ##+############################################################################ ##+################################# ## SET THE TOP WINDOW NAME. ##+################################# wm title . "tkDegreesMinSec - Converter / Selector" wm iconname . "DegMinSec" # 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_COLORSEL_GEOM)" } ##+####################################################################### ## SET COLOR SCHEME (palette) FOR THE WINDOW. ##+####################################################################### if {0} { ## 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 } if {1} { ## Brownish palette set Rpal255 255 set Gpal255 180 set Bpal255 100 } set hexCOLORpal [format "#%02X%02X%02X" $Rpal255 $Gpal255 $Bpal255] tk_setPalette $hexCOLORpal ## Set color background for some widgets. set scaleBKGD "#f0f0f0" set textBKGD "#f0f0f0" # set entryBKGD "#f0f0f0" # set radbuttBKGD "#c0c0c0" # set chkbuttBKGD "#c0c0c0" # set listboxBKGD "#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_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 PADY_button 0 set PADX_button 0 set BDwidthPx_button 2 ## For LABEL widgets: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 ## For TEXT widgets: set BDwidthPx_text 2 ## SCALE geom parameters: set BDwidthPx_scale 2 set scaleLengthPx 300 set scaleThicknessPx 10 ## CANVAS geom parms: set initCanWidthPx 200 set initCanHeightPx 200 set minCanHeightPx 24 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 ## For ENTRY widgets: # set BDwidth_entry 2 ##+################################################################### ## 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), and perhaps ## some label widgets showing angle info. ## ## For HEIGHT, allow ## 1 chars high for the '.fRbuttons' frame ## 2 chars high for the '.fRleft.fRsliders1' frame ## 6 chars high for the '.fRleft.fRsliders3' frame ##+####################################################################### ## If we allowed the window to be resizable, we could pack the canvas with ## '-fill both -expand 1' so that the canvas can be enlarged by ## enlarging the window. ##+####################################################################### set minWinWidthPx [font measure fontTEMP_varwidth \ " UseIt Cancel Help Decimal-degrees: 60.000 Deg-Min-Sec: 60 0 0"] ## 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 chars high for the '.fRbuttons' frame ## 2 chars high for the '.fRleft.fRsliders1' frame ## 6 chars high for the '.fRleft.fRsliders3' frame ##+############################################################# set CharHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {9 * $CharHeightPx}] ## Add about 28 pixels for top-bottom window decoration, ## about 3 frames x 4 pixels/frame for each of the 3 stacked frames/widgets ## and their widgets (their borders/padding). set minWinHeightPx [expr {40 + $minWinHeightPx}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ##+############################################# ## If you want to make the window un-resizable, ## you can use the following statement. ##+############################################# # wm resizable . 0 0 ##+######################################################################## ## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI. ## NOTE: This can aid INTERNATIONALIZATION. This array can ## be set according to a nation/region parameter. ##+######################################################################## ## if { "$VARlocale" == "en"} ## For '.fRbuttons' frame: set aRtext(buttonUSEIT) "UseIt" set aRtext(buttonCANCEL) "Cancel" set aRtext(buttonHELP) "Help" set aRtext(labelTEXTdecdeg) " Decimal-Degrees:" # set aRtext(labelTEXTdegminsec) " Degrees-Minutes-Seconds:" set aRtext(labelTEXTdegminsec) " Deg-Min-Sec:" ## For '.fRsliders1' frame: set aRtext(labelSCALEdecdeg) "Decimal Degrees:" ## For '.fRsliders3' frame: set aRtext(labelSCALEintdeg) "Integer Degrees:" set aRtext(labelSCALEintmin) "Integer Minutes:" set aRtext(labelSCALEsec) "Integer Seconds:" ## END OF if { "$VARlocale" == "en" ##+#################################################################### ##+#################################################################### ## DEFINE *ALL* THE FRAMES: ## TOP-LEVEL FRAMES: ## '.fRbuttons' - to contain several buttons (UseIt,Cancel,etc), ## as well as 2 label & 2 text widgets to ## display the current angle. ## '.fRleft' - for 4 scale widgets ## '.fRright' - for canvas widget ## ## SUB-FRAMES: ## '.fRleft.fRsliders1' - to contain 1 sliderbar. ## '.fRleft.fRsliders3' - to contain 3 sliderbars. ##+#################################################################### ##+#################################################################### ## 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 .fRleft -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRright -relief $RELIEF_frame -borderwidth $BDwidthPx_frame # -height 140 -width 140 # frame .fRleft.fRsliders1 -relief $RELIEF_frame -borderwidth $BDwidthPx_frame frame .fRleft.fRsliders1 -relief raised -borderwidth 2 frame .fRleft.fRsliders3 -relief $RELIEF_frame -borderwidth $BDwidthPx_frame # frame .fRleft.fRsliders3 -relief raised -borderwidth 2 frame .fRleft.fRsliders1.fRdecdeg -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRleft.fRsliders3.fRdeg -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRleft.fRsliders3.fRmin -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRleft.fRsliders3.fRsec -relief $RELIEF_frame -bd $BDwidthPx_frame ##+######################################################## ## PACK the TOP-LEVEL FRAMES. ##+######################################################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRleft \ -side left \ -anchor nw \ -fill x \ -expand 0 pack .fRright \ -side left \ -anchor nw \ -fill x \ -expand 0 ##+########################################## ## PACK the 2nd-LEVEL FRAMES. ##+########################################## pack .fRleft.fRsliders1 \ .fRleft.fRsliders3 \ -side top \ -anchor nw \ -fill both \ -expand 0 ##+########################################## ## PACK the 3rd-LEVEL FRAMES. ##+########################################## pack .fRleft.fRsliders1.fRdecdeg \ -side top \ -anchor w \ -fill x \ -expand 0 pack .fRleft.fRsliders3.fRdeg \ .fRleft.fRsliders3.fRmin \ .fRleft.fRsliders3.fRsec \ -side top \ -anchor w \ -fill x \ -expand 0 ##+################################################################ ## The frames are defined and packed. ##+################################################################ ## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES. ##+################################################################ ##+################################################################ ##+######################################################## ## IN THE '.fRbuttons' frame -- DEFINE several BUTTONS --- ## 1 'UseIt' BUTTON, 1 'Cancel' BUTTON, and 1 'Help' BUTTON. ## Also define a pair of LABEL and TEXT widgets. ## THEN PACK THE WIDGETS. ##+######################################################## button .fRbuttons.buttUSEIT \ -text "$aRtext(buttonUSEIT)" \ -font fontTEMP_button \ -padx $PADX_button \ -pady $PADY_button \ -relief raised \ -bd $BDwidthPx_button \ -command {put_vars} button .fRbuttons.buttCANCEL \ -text "$aRtext(buttonCANCEL)" \ -font fontTEMP_button \ -padx $PADX_button \ -pady $PADY_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .fRbuttons.buttHELP \ -text "$aRtext(buttonHELP)" \ -font fontTEMP_button \ -padx $PADX_button \ -pady $PADY_button \ -relief raised \ -bd $BDwidthPx_button \ -command {popup_msg_var_scroll "$HELPtext"} label .fRbuttons.labDECDEG \ -text "$aRtext(labelTEXTdecdeg)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label # -width 11 \ text .fRbuttons.txtDECDEG \ -relief raised \ -borderwidth $BDwidthPx_text \ -height 1 \ -width 8 \ -wrap none \ -font fontTEMP_text label .fRbuttons.labDEGMINSEC \ -text "$aRtext(labelTEXTdegminsec)" \ -font fontTEMP_label \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label # -width 8 \ text .fRbuttons.txtDEGMINSEC \ -relief raised \ -borderwidth $BDwidthPx_text \ -height 1 \ -width 11 \ -wrap none \ -font fontTEMP_text ## Pack the widgets in frame '.fRbuttons'. pack .fRbuttons.buttUSEIT \ .fRbuttons.buttCANCEL \ .fRbuttons.buttHELP \ -side left \ -anchor center \ -fill none \ -expand 0 pack .fRbuttons.txtDEGMINSEC \ .fRbuttons.labDEGMINSEC \ .fRbuttons.txtDECDEG \ .fRbuttons.labDECDEG \ -side right \ -anchor center \ -fill none \ -expand 0 ##+######################################################## ## IN THE '.fRleft.fRsliders1' frame -- ## DEFINE 1 LABELS and 1 SCALE WIDGETS. THEN PACK THEM. ##+######################################################## set labelCHARS 8 label .fRleft.fRsliders1.fRdecdeg.label \ -text "$aRtext(labelSCALEdecdeg)" \ -font fontTEMP_label \ -width $labelCHARS \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label scale .fRleft.fRsliders1.fRdecdeg.scale \ -orient horizontal \ -from 0.000 -to 360.000 \ -resolution 0.001 \ -digits 6 \ -length $scaleLengthPx \ -variable VARdecdeg \ -font fontTEMP_scale \ -showvalue true \ -bd $BDwidthPx_scale \ -width $scaleThicknessPx ## -command {angle_update} # .fRleft.fRsliders1.fRdecdeg.scale set $VARdecdeg ##+#################################################### ## PACK 1 LABEL & 1 SCALE IN '.fRleft.fRsliders3' FRAME. ##+#################################################### pack .fRleft.fRsliders1.fRdecdeg.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRsliders1.fRdecdeg.scale \ -side left \ -anchor w \ -fill x \ -expand 1 ##+######################################################## ## IN THE '.fRleft.fRsliders3' frame -- ## DEFINE 3 LABELS and 3 SCALE WIDGETS. THEN PACK THEM. ##+######################################################## ## INTEGER DEGREES: label .fRleft.fRsliders3.fRdeg.label \ -text "$aRtext(labelSCALEintdeg)" \ -width $labelCHARS \ -font fontTEMP_label \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label scale .fRleft.fRsliders3.fRdeg.scale \ -orient horizontal \ -from 0 -to 360 \ -resolution 1 \ -digits 0 \ -length $scaleLengthPx \ -variable VARintdeg \ -font fontTEMP_scale \ -showvalue true \ -bd $BDwidthPx_scale \ -width $scaleThicknessPx ## -command {angle_update} # .fRleft.fRsliders3.fRdeg.scale set $VARintdeg ## INTEGER MINUTES: label .fRleft.fRsliders3.fRmin.label \ -text "$aRtext(labelSCALEintmin)" \ -width $labelCHARS \ -font fontTEMP_label \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label scale .fRleft.fRsliders3.fRmin.scale \ -orient horizontal \ -from 0 -to 60 \ -resolution 1 \ -digits 0 \ -length $scaleLengthPx \ -variable VARintmin \ -font fontTEMP_scale \ -showvalue true \ -bd $BDwidthPx_scale \ -width $scaleThicknessPx ## -command {angle_update} # .fRleft.fRsliders3.fRmin.scale set $VARintmin ## INTEGER SECONDS: label .fRleft.fRsliders3.fRsec.label \ -text "$aRtext(labelSCALEsec)" \ -width $labelCHARS \ -font fontTEMP_label \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label scale .fRleft.fRsliders3.fRsec.scale \ -orient horizontal \ -from 0 -to 60 \ -resolution 1 \ -digits 0 \ -length $scaleLengthPx \ -variable VARsec \ -font fontTEMP_scale \ -showvalue true \ -bd $BDwidthPx_scale \ -width $scaleThicknessPx ## -command {angle_update} # .fRleft.fRsliders3.fRsec.scale set $VARsec ##+##################################################### ## PACK 3 LABELS & 3 SCALES IN '.fRleft.fRsliders' FRAME. ##+##################################################### pack .fRleft.fRsliders3.fRdeg.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRsliders3.fRdeg.scale \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRleft.fRsliders3.fRmin.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRsliders3.fRmin.scale \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRleft.fRsliders3.fRsec.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRsliders3.fRsec.scale \ -side left \ -anchor w \ -fill x \ -expand 1 ##+###################################################### ## In the '.fRright' frame - ## DEFINE-and-PACK 1 CANVAS widget. ##+###################################################### ## We set '-highlightthickness' and '-borderwidth' to ## zero, to avoid covering some of the viewable area ## of the canvas, as suggested on page 558 of the 4th ## edition of 'Practical Programming with Tcl and Tk'. ##+################################################### canvas .fRright.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 \ pack .fRright.can \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+####################################### ## END OF MAIN SECTION TO SETUP THE GUI. ## FRAMES AND WIDGETS ARE DEFINED. ##+####################################### ##+##################################################################### ##+##################################################################### ## BINDINGS section: bindings on the 4 sliders ##+##################################################################### ##+##################################################################### bind .fRleft.fRsliders1.fRdecdeg.scale \ {set TYPEchange "decdeg" ; angle_update 0} bind .fRleft.fRsliders3.fRdeg.scale \ {set TYPEchange "degminsec" ; angle_update 0} bind .fRleft.fRsliders3.fRmin.scale \ {set TYPEchange "degminsec" ; angle_update 0} bind .fRleft.fRsliders3.fRsec.scale \ {set TYPEchange "degminsec" ; angle_update 0} ##+##################################################################### ##+##################################################################### ## DEFINE PROCEDURES: ## 'angle_update' - for button1-release bindings (or '-command' ## options) on 4 scale widgets --- and ## called in the 'Additional GUI Initialization' ## section at the bottom of this script. ## ## 'draw_circle' - called in the 'Additional GUI Initialization' ## section at the bottom of this script. ## ## 'redraw_angle' - called by 'angle_update' proc ## ## 'put_vars' - called by UseIt button ## 'popup_msg_var_scroll' - called by Help button ##+##################################################################### ##+##################################################################### ##+##################################################################### ## PROC: 'angle_update' ##+##################################################################### ## ## PURPOSE: As any slider is changed, set ## 1) the other sliders (their variables) ## 2) update the text widgets that hold the angle in 2 forms ## 3) update the angle on the circle on the canvas. ## ## To set variable ## 1) VARdecdeg from VARintdeg,VARintmin,VARsec ## OR set variables ## 2) VARintdeg,VARintmin,VARsec from VARdecdeg ## as any slider is changed. ## ## A change-indicator (global var 'TYPEchange') is used to ## indicate which of these 2 scenarios is to be performed. ## The values for 'chgIND' are 'decdeg' or 'degminsec'. ## ## CALLED BY: a change in any of 4 scale widgets: ## - .fRleft.fRsliders1.fRdecdeg.scale ## - .fRleft.fRsliders3.fRdeg.scale ## - .fRleft.fRsliders3.fRmin.scale ## - .fRleft.fRsliders3.fRsec.scale ## via a button1-release binding (or '-command') ## on these scale widgets. ##+##################################################################### proc angle_update {x} { ## NOTE: We supply variable 'x' just in case we ever decide to ## call this proc in a '-command' option of the scale widgets. ## Dummy out this proc, for testing. # return global TYPEchange VARdecdeg VARintdeg VARintmin VARsec ## If TYPEchange = 'decdeg', calculate VARintdeg VARintmin VARsec. if {"$TYPEchange" == "decdeg"} { set VARintdeg [expr {int($VARdecdeg)}] ## Multiply the fractional part of VARdecdeg by 60 and take the integer part. set VARintmin [expr {int(($VARdecdeg - $VARintdeg) * 60.0)}] ## Multiply the fractional part of (VARdecdeg - VARintdeg - VARintmin/60) by 60 ## --- and take the integer part. (Alternative: use fractional seconds) set VARsec [expr {int( (($VARdecdeg - $VARintdeg) - ($VARintmin / 60.0)) * 3660.0) }] } ## If TYPEchange = 'degminsec', calculate VARdecdeg. if {"$TYPEchange" == "degminsec"} { set VARdecdeg [expr {double($VARintdeg)}] set VARdecdeg [expr {$VARdecdeg + ($VARintmin/60.0)}] set VARdecdeg [expr {$VARdecdeg + ($VARsec/3600.0)}] } ########################################################## ## Update the text widgets that hold the angle in 2 forms. ########################################################## .fRbuttons.txtDECDEG delete 1.0 end .fRbuttons.txtDECDEG insert end $VARdecdeg .fRbuttons.txtDEGMINSEC delete 1.0 end .fRbuttons.txtDEGMINSEC insert end "$VARintdeg $VARintmin $VARsec" ################################################### ## Update the angle on the circle on the canvas. ################################################### redraw_angle $VARdecdeg } ## END of proc 'angle_update' ##+##################################################################### ## 'draw_circle' PROCEDURE ##+##################################################################### ## ## PURPOSE: To draw a circle in the middle of the canvas. ## ## CALLED: in the 'Additional GUI Initialization' section at the ## bottom of this script. ##+##################################################################### proc draw_circle {} { # global ## Get the current width & height of the canvas (in pixels). set canWidthPx [winfo width .fRright.can] set canHeightPx [winfo height .fRright.can] ## FOR TESTING: # puts "canWidthPx : $canWidthPx" # puts "canHeightPx: $canHeightPx" set ULXpx [expr {0.1 * $canWidthPx }] set ULYpx [expr {0.1 * $canHeightPx}] set LRXpx [expr {0.9 * $canWidthPx }] set LRYpx [expr {0.9 * $canHeightPx}] ## FOR TESTING: # puts "ULXpx: $ULXpx" # puts "ULYpx: $ULYpx" # puts "LRXpx: $LRXpx" # puts "LRYpx: $LRYpx" .fRright.can create oval \ $ULXpx $ULYpx $LRXpx $LRYpx \ -width 2 -outline black ## -fill black } ## END of proc 'draw_circle' ##+##################################################################### ## 'redraw_angle' PROCEDURE ##+##################################################################### ## ## PURPOSE: To (re)draw an angle on the circle in the middle of the canvas. ## ## CALLED BY: the 'angle_update' proc ##+##################################################################### proc redraw_angle {degrees} { ## Get the current width & height of the canvas (in pixels). set canWidthPx [winfo width .fRright.can] set canHeightPx [winfo height .fRright.can] ## FOR TESTING: # puts "canWidthPx : $canWidthPx" # puts "canHeightPx: $canHeightPx" set ULXpx [expr {0.1 * $canWidthPx }] set ULYpx [expr {0.1 * $canHeightPx}] set LRXpx [expr {0.9 * $canWidthPx }] set LRYpx [expr {0.9 * $canHeightPx}] .fRright.can delete TAGarc ## FOR TESTING: # puts "ULXpx: $ULXpx" # puts "ULYpx: $ULYpx" # puts "LRXpx: $LRXpx" # puts "LRYpx: $LRYpx" .fRright.can create arc \ $ULXpx $ULYpx $LRXpx $LRYpx -start 0 -extent $degrees \ -width 2 -fill black -outline black -tag TAGarc } ## END of proc 'redraw_angle' ##+##################################################################### ## PROC: 'put_vars' ## ## PURPOSED: Put decimal-degrees and degrees-minutes-seconds ## to standard output. ## ## CALLED BY: button .fRbuttons.buttUSEIT ##+##################################################################### proc put_vars { } { global VARdecdeg VARintdeg VARintmin VARsec puts "$VARdecdeg $VARintdeg $VARintmin $VARsec" exit } ## END of proc 'puts_vars' ##+######################################################################## ## PROC 'popup_msg_var_scroll' ##+######################################################################## ## PURPOSE: Report help or error conditions to the user. ## 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_msg_var_scroll { VARtext } { ## global fontTEMP_text #; 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 .topmsg} toplevel .topmsg # wm geometry .topmsg 600x400+100+50 wm geometry .topmsg +100+50 wm title .topmsg "Note" # wm title .topmsg "Note to $env(USER)" wm iconname .topmsg "Note" ##################################### ## DEFINE & PACK TEXT WIDGET. ##################################### text .topmsg.text \ -wrap none \ -font fontTEMP_text \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand ".topmsg.scrolly set" \ -xscrollcommand ".topmsg.scrollx set" scrollbar .topmsg.scrolly \ -orient vertical \ -command ".topmsg.text yview" scrollbar .topmsg.scrollx \ -orient horizontal \ -command ".topmsg.text xview" ## Pack the scrollbars BEFORE the text widget, ## so that the text does not monopolize the space. pack .topmsg.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 .topmsg.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 .topmsg.text \ -side top \ -anchor center \ -fill both \ -expand 1 ##################################### ## DEFINE & PACK OK BUTTON WIDGET. ##################################### button .topmsg.butt \ -text "OK" \ -command "destroy .topmsg" pack .topmsg.butt \ -side bottom \ -anchor center \ -fill none \ -expand 0 ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## .topmsg.text delete 1.0 end .topmsg.text insert end $VARtext .topmsg.text configure -state disabled } ## END OF PROC 'popup_msg_var_scroll' ##+############################################### ## END OF PROCS SECTION. ##+############################################### ## SET HELP TEXT variable. ##+############################################### set HELPtext \ " ************ tkDegreesMinSec - Converter / Selector Utility ********* This Tk GUI script provides a GUI for CONVERTING between an angle in - DEGREES-MINUTES-SECONDS form (Example: 60 10 20) and an angle in - DECIMAL-DEGREES form (Example: 60.173). This GUI is also for SELECTING the chosen degrees value by clicking on a 'UseIt' button. (This is useful if this utility script is called from within another software utility.) When you click on the 'UseIt' button, both forms of the degrees value (decimal and degrees-minutes-seconds) are sent to 'stdout' (standard output). Thus this utility Tk script can be called from within a shell script or another Tk script to get an angle between 0 and 360 degrees --- in either form. ******************* METHOD OF OPERATION: ******************* There are 4 'scale' widgets on the GUI: - ONE 'scale' for decimal degrees (range: 0.000 up to 360.000) and - THREE 'scales' (in integers) for - degrees (range: 0 up to 360) - minutes (range: 0 up to 60) - seconds (range: 0 up to 60) (Alternatively, the Tk script and GUI could be changed to allow for fractional 'seconds' -- say, 0.00 to 60.00) --- To give some visual feedback of the angle size: For any slider bar (scale) change, this GUI script depicts the angle in a 'canvas' widget --- on which a circle is drawn and the size of the angle is shown via a color-filled sector in the circle. The canvas widget is on one side of the GUI window (covering about 20% of the window). Also the GUI shows the current angle's decimal and degrees-minutes-seconds values in two small text widgets about 12 characters long. A user can 'swipe/highlight-and-paste' either text string into another window --- such as a text editor window. ********* SOME USES: ********* 1) The GUI is useful for CONVERTING an angle in degrees-minutes-seconds form to decimal form --- or vice versa. 2) This Tk script accepts an angle (in decimal form or in degrees-minutes-seconds form) to initialize the 4 scale widgets. This facilitates using this Tk script as an angle selector or changer utility within another software utility. IN A SHELL SCRIPT OR ANOTHER TK SCRIPT, this Tk script can ACT AS AN ANGLE CONVERTER-SELECTOR by calculating and outputting a currently selected angle (in both decimal and deg-min-sec form) to 'stdout' (standard output), when the 'UseIt' button is clicked. Example output string: 60.000 60 0 0 ************************** ACCURACY (down to seconds) ************************** We note here that if you measure an angle down to the 'seconds' level, we are talking about an accuracy to the 3rd or 4th decimal digit --- when we express the angle in decimal form. For example, consider the two angles 60 degrees 0 minutes 0 seconds and 60 degrees 0 minutes 1 second To convert the 1 second to degrees, we divide by 3600 --- and 1 / 3600 = 0.000277778. So a second gives us decimal accuracy down to 3 decimal digits, but not quite to 4 decimal digits. In fact, it takes between 3 and 4 seconds (of angle) to 'turn over' the 3rd decimal digit. In other words, we can ask the question how many times must we add 0.277778 to get 1.0. The answer is given by 1 / 0.277778 = 3.599997 ~ 3.6 --- So we see that a second of angle gives us better than 3 decimal digit accuracy. Let us give this a little better intuitive meaning. How does this translate into linear measure? Note that in our example of an angle near 60 degrees, the percent accuracy of a one second divergence from 60 is 100 * (60.000277778 - 60) / 60 = 0.0277778 / 60 = 0.000462963 that is, a little over 4 ten-thousands of a percent. --- Let us take that percent of a mile, which is 5,280 feet: 0.000462963 * 5280 / 100 = 0.024444446 of a foot. 0.024444446 * 12 inches = 0.293333357 inch So the accuracy of a one second angle (when measuring angles between 0 and 360 degrees) corresponds roughly to measuring a mile to an accuracy of less than one-third of an inch. --- If we re-do these calculations in metric form: Let us take that percent of a kilomter, which is 1,000 meters: 0.000462963 * 1000 / 100 = 0.00462963 of a meter = 0.462963 centimeters So the accuracy of a one second angle (when measuring angles between 0 and 360 degrees) corresponds roughly to measuring a kilometer to an accuracy of less than half of a centimeter. ****************** BABYLONIAN NUMBERS ****************** When you read various math books that popularize or give the history of number theory, you see that the measure of angles in terms of degrees-minutes-seconds comes from the Babylonians who used a number system based on 60 rather than 10. They probably chose this base for their numbers because it gives them a lot of flexibility when dealing with fractions --- that is, when dividing things up --- such as the estate of fathers among families of various sizes --- 2 children, 3 children, 4 children, 5 children, 6 children, 10 children, 15 children, ... Sixty is 'nice' in that it can be factored into 2 x 2 x 3 x 5. When you divide something into 60 pieces, you can group the pieces into equal piles in many different ways: - 2 piles of 30 pieces - 3 piles of 20 pieces - 4 piles of 15 pieces - 5 piles of 12 pieces - 6 piles of 10 pieces - 7 piles of ... whoops! Well, sixty does quite nicely --- as far as it goes. I can see why the Babylonians did not want to base their number system on 7 x 60 = 420. You can do web searches on keywords such as 'babylonian number system sixty' or 'babylonian number degrees astronomy' to look for more information on this topic. " ##+############################################### ## ADDITONAL GUI INITIALIZATION (if any) FOLLOWS. ##+############################################### ######################################################### ## Get the canvas dimensions and draw the initial circle. ######################################################### update draw_circle ##+######################################################################## ##+######################################################################## ## GET (one or three) INPUT PARM VALUES: ## ## (with which to set initial scale widget values --- ## VARdecdeg or VARintdeg VARintmin VARsec) ## ##+######################################################################## ##+######################################################################## ## Example argc/argv processing: ## ## if {$argc == 0} { ## set VARtext "$env(CONFIRM_TEXT)" ## } else { ## set VARtext [lindex $argv 0] ## } ##+######################################################################## if {$argc == 3} { set VARintdeg [lindex $argv 0] set VARintmin [lindex $argv 1] set VARsec [lindex $argv 2] set TYPEchange "degminsec" angle_update 0 } elseif {$argc == 1} { set VARdecdeg [lindex $argv 0] set TYPEchange "decdeg" angle_update 0 } else { ## If no angle(s) have been supplied, use a default. set VARdecdeg 60 set TYPEchange "decdeg" angle_update 0 }