#!/usr/bin/wish -f ##+######################################################################## ## ## SCRIPT: tkCompoundedGrowthCalculator.tk ## ## PURPOSE: This Tk script is a calculator that allows one to easily compute ## compounded growth --- for example, ## - in a bank account for a fixed interest rate ## or ## - in a population for a fixed net growth rate. ## or ## - in a population for a fixed net decline rate (negative growth). ## ## This calculator computes a result based on several inputs: ## - INIT, an initial value (such a bank account or population) ## - R, a growth (or decline) rate (per time period, such as per year) ## - N, the number of time periods, the end of which is the ## time point at which the result, TOT, is calculated. ## ## The calculation is summarized by ## TOT = INIT * (1 + R)^N ## ##+######################### ## PLANNED LAYOUT OF THE GUI: ## ## FrameNames ## VVVVVVVVVV ## ----------------------------------------------------------------------------- ## tkCompoundedGrowthCalculator ## [window title] ## ----------------------------------------------------------------------------- ## ## .fRbuttons {Exit} {Help} {CalculateTotal} ## ## .fRinputs1 Initial value: 100.0______ (dollars, population, ...) ## ## .fRinputs2 Growth (or decline) ## Rate (percent): 3.0______ (such as 10 or -10 for a ten-percent rate) ## ## .fRinputs3 Number of time periods (N): 50___ (such as years, if the rate is per year) ## ## .fRmsg [... Messages to the user can be displayed here on a label widget ...] ## ## .fRresult Total after N ## time periods: [ Calculated result is shown in a text widget here. ] ## ## .fRplotcntl {Plot value of {Backgd {Line {Title-Labels {TicMarks ## INIT*(1 + R/100)^N} Color} Color} Color} Color} ## ## .fRplot ----------------------------------------------------------------------------- ## | | ## | | ## | | ## | 'Canvas' for displaying a plot of INIT*(1 + R/100)^N against N. | ## | | ## | | ## | | ## | | ## | | ## ----------------------------------------------------------------------------- ## ## SKETCH CONVENTIONS for this GUI sketch: ## ## 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. ## 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 horizontal Tk 'scale' widget (if any). ## ##+############################### ## ON SCALE WIDGETS and SCROLLBARS: ##+############################### ## ## A LINE (HYPHENS or VERTICAL-BARS) WITH AN 'ARROW-HEAD' AT EACH END indicates ## a Tk 'scale' widget. ## ## A combination of VERTICAL-BAR CHARACTERS AND HYPHEN (or UNDERSCORE) CHARACTERS, ## that outline a RECTANGULAR SHAPE, are used to indicate either a Tk 'canvas' ## widget or a Tk 'listbox' widget or a multi-line Tk 'text' widget. ## ## SCROLL-BAR 'ARROW-HEADS' (for a 'canvas', 'listbox', or 'text' Tk widget) ## are drawn as follows: ## ## UP ARROW-HEAD is drawn with a CAPITAL-A. ## DOWN ARROW-HEAD is drawn with a CAPITAL-V. ## LEFT ARROW-HEAD is drawn with a LESS-THAN sign. ## RIGHT ARROW-HEAD is drawn with a GREATER-THAN sign. ## ## UP-and-DOWN ARROW-HEADS at the right/left of the box shape indicate ## a VERTICAL SCROLL-BAR there. ## ## LEFT-and-RIGHT ARROW-HEADS at the bottom/top of the box shape indicate ## a HORIZONTAL SCROLL-BAR there. ## ## The arrow-heads on a horizontal scrollbar are joined by hyphens, rather than ## underscores. ## ##+################## ## GUI WIDGET SUMMARY: ## ## This GUI will contain about: ## ## 8 'button' widgets ## 8 'label' widgets ## 3 'entry' widgets ## 1 'text' widget ## 1 'canvas' widget (with x-y scrollbars) ## 0 'checkbutton' widgets ## 0 'radiobutton' widgets ## 0 'scale' widgets ## 0 'listbox' widgets ## ##+################################## ## METHOD USED to perform the plot: ## ## In 'world coordinates' (not pixel coordinates), the ## x-axis for this plot goes from 0.0 to about N (time periods) ## --- with integer-time-period values plotted along the x-axis. ## The y-axis goes from 0.0 to about the max of INIT and TOT, MAX. ## (We assume INIT is positive.) ## ## A utility proc is used apply 'world coordinates' to ## the pixel coordinates of the Tk canvas. The corners ## of the plot area in 'world coordinates' are mapped to ## the corners of the plot area in 'pixel coordinates'. ## ## To allow some margin around the rectangular plot area, ## the world coordinates of the corners of the ## rectangular canvas plot area will be (approximately) ## - top left (-[0.1*N],[1.1*MAX]) ## - bottom right (+[1.1*N],-[0.1*MAX]) ## ## Two procs -- 'Xwc2px' and 'Ywc2px' -- are used to convert world ## coordinates to pixel coordinates that are to be used in the ## 'create line', 'create text', and 'create oval' commands. ## ##+####################### ## CAPTURING THE GUI IMAGE: ## ## A screen/window capture utility (like 'gnome-screenshot' ## on Linux) can be used to capture the GUI image in a PNG ## or GIF file, say. ## ## If necessary, an image editor (like 'mtpaint' on Linux) ## can be used to crop the window capture image. The image ## could also be down-sized --- say to make a smaller image ## suitable for use in a web page or an email. ## ##+####################################################################### ## '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, if any). ## 1b) Pack the frames. ## ## 2) Define & pack all widgets in the frames, frame by frame. ## After all the widgets for a frame are defined, pack them in the frame. ## ## 3) Define keyboard or mouse/touchpad/touch-sensitive-screen 'event' ## BINDINGS, if needed. ## ## 4) Define PROCS, if needed. ## ## 5) Additional GUI INITIALIZATION (typically with one or more of ## the procs), if needed. ## ##+################################# ## Some detail of the code structure of this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : ## '.fRbuttons' - to contain 'Exit', 'Help', 'Calculate' buttons. ## ## '.fRinputs1' - to contain a label, an entry, and another label widget. ## ## '.fRinputs2' - to contain a label, an entry, and another label widget. ## ## '.fRinputs3' - to contain a label, an entry, and another label widget. ## ## '.fRmsg' - to contain a label widget. ## ## '.fRresult' - to contain a label and text widget pair. ## ## '.fRplotcntl' - to contain 5 button widgets. ## ## '.fRplot ' - to contain a canvas widget, which will display ## the plot. ## ## 1b) Pack ALL frames. ## ## 2) Define & pack all widgets in the frames -- basically going through ## frames & their interiors in left-to-right, or top-to-bottom order. ## ## 3) Define BINDINGS: if any (see BINDINGS code section below) ## ## 4) Define PROCS: ## ## 'calculate' - Called by the 'Calculate' button. ## ##**************************** ## Some Drawing Utility procs: ##*************************** ## ## 'setMappingVars_for_px2wc' - Called by the 'draw_plot' proc below. ## ## Set up constants to be used in converting ## a 'world coordinate' to a 'pixel coordinate' ## on the Tk canvas --- for the 'draw_plot' proc. ## ## This proc takes the coordinates of an UpperLeft (UL) ## point and a LowerRight (LR) point --- in BOTH ## 'world coordinates' and 'pixel coordinates' and ## sets some global variables to be used by the ## other drawing procs --- mainly the ratio: ## ## the number-of-pixels-per-world-coordinate-unit, ## global variable 'PXperWC' ## ## 'Xwc2px' - To convert an x world-coordinate to pixels, ## to be used by the following 'draw_plot' proc. ## ## 'Ywc2px' - To convert a y world-coordinate to pixels, ## to be used by the following 'draw_plot' proc. ## ## (Note: These 2 procs are used in the 'draw_plot' proc ## below to perform conversion from 'world coordinates' ## to pixels. The global variables, that were set by a ## call to the proc 'setMappingVars_for_px2wc', are used ## to do the conversion.) ## ##***************************** ## End of Drawing utility procs. ##***************************** ## ## 'draw_plot' - Called by the 'Plot' button. ## ##*************************** ## Color Button utility procs: ##*************************** ## ## 'set_background_color' - Called by the background color button. ## ## 'set_line_color' - Called by the plot lines (and points) color button. ## ## 'set_title-labels_color' - Called by the plot title (and labels) color button. ## ## 'set_ticmarks_color' - Called by the ticmarks color button. ## ## 'update_button_colors' - Sets the color of the 4 color buttons. ## ##********************************** ## End of Color Button utility procs. ##********************************** ## ## 'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var. ## ## ## 5) Additional GUI Initialization: ## - sets some initial values of various parameters, such as the ## initial setting of the entry widgets. ## ##+####################################################################### ## 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 ## but this script should work in most previous 8.x versions, and probably ## even in some 7.x versions (if font handling is made 'old-style'). ##+####################################################################### ## MAINTENANCE HISTORY: ## Started by: Blaise Montandon 2016mar20 ## Changed by: Blaise Montandon 2016 ##+######################################################################## ##+###################################################### ## Set WINDOW TITLE and POSITION. ##+###################################################### wm title . "Compounded Growth (or Decline) Computation" wm iconname . "CompoundedGrowth" wm geometry . +8+30 ##+###################################################### ## Set the COLOR SCHEME for the window and its widgets --- ## such as entry field background color. ##+###################################################### tk_setPalette "#f0f0f0" set entryBKGD "#ffffff" # set scaleBKGD "#f0f0f0" # set radbuttBKGD "#c0c0c0" # set chkbuttBKGD "#c0c0c0" # set listboxBKGD "#ffffff" ##+######################################################## ## DEFINE (temporary) FONT NAMES. ## ## We use a VARIABLE-WIDTH font for text on LABEL and ## BUTTON widgets. ## ## We use a FIXED-WIDTH font for LISTBOX lists, ## for Help-text in a TEXT widget, and for ## the text in ENTRY fields, if any. ##+######################################################## font create fontTEMP_varwidth \ -family {comic sans ms} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_varwidth \ -family {comic sans ms} \ -size -12 \ -weight bold \ -slant roman ## Some other possible (similar) variable width fonts: ## Arial ## Bitstream Vera Sans ## DejaVu Sans ## Droid Sans ## FreeSans ## Liberation Sans ## Nimbus Sans L ## Trebuchet MS ## Verdana font create fontTEMP_fixedwidth \ -family {liberation mono} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_fixedwidth \ -family {liberation mono} \ -size -12 \ -weight bold \ -slant roman ## Some other possible fixed width fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## DejaVu Sans Mono ## Droid Sans Mono ## FreeMono ## Nimbus Mono L ## TlwgMono ##+########################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. width and height of canvas, and padding for Buttons) ##+########################################################### ## LABEL widget geom settings: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 set RELIEF_label_lo "flat" ## BUTTON widget geom settings: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## We generally default to relief "raised" for all 'button' widgets. ## BUT, in case you want to experiment: set RELIEF_button "raised" ## ENTRY widget geom settings: set BDwidthPx_entry 2 ## TEXT widget geom settings: set BDwidthPx_text 2 ## CANVAS widget geom settings: set initCanWidthPx 300 set initCanHeightPx 300 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 set RELIEF_canvas "flat" ## RADIOBUTTON geom parameters: # set PADXpx_radbutt 0 # set PADYpx_radbutt 0 # set BDwidthPx_radbutt 1 # set RELIEF_radbutt_hi "raised" ## CHECKBUTTON geom parameters: # set PADXpx_chkbutt 0 # set PADYpx_chkbutt 0 # set BDwidthPx_chkbutt 1 # set RELIEF_chkbutt_hi "raised" ## SCALE widget geom parameters: # set BDwidthPx_scale 2 # set scaleThicknessPx 10 ##+############################################################## ## 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 the '.fRbuttons' frame: set aRtext(buttonEXIT) "Exit" set aRtext(buttonHELP) "Help" set aRtext(buttonCALC) "Calculate" set aRtext(labelFORMULA) "TOT = INIT * (1 + R/100)^N" ## For the '.fRinputs1' frame: set aRtext(labelINITvalue) "Initial value (INIT):" set aRtext(labelINITdesc) "(investment in dollars, population in billions, ...)" ## For the '.fRinputs2' frame: set aRtext(labelRATEvalue) "Growth (or decline) Rate, R (percent):" set aRtext(labelRATEdesc) "(such as 10 or -10 for a ten-percent rate)" ## For the '.fRinputs3' frame: set aRtext(labelNperiods) "Number of time periods (N):" set aRtext(labelNdesc) "(such as years, if the rate is per year)" ## For the '.fRresult' frame: set aRtext(labelRESULT) "RESULT - TOTal after N time periods:" set aRtext(labelRESULTdesc) "(dollars, billions, ...)" ## For the '.fRplotcntl' frame: set aRtext(buttonPLOT) "PLOT graph of INIT*(1 + R/100)^N" set aRtext(buttonCOLORbkgd) "Backgrnd Color" set aRtext(buttonCOLORline) "Line Color" set aRtext(buttonCOLORtitle) "Title-Labels Color" set aRtext(buttonCOLORticmarks) "TicMarks Color" ## END OF if { "$VARlocale" == "en"} ##+###################################################################### ## Set a MIN-SIZE of the window (roughly). ## ## For WIDTH, allow for the min-width of a buttons frame. ## ## For HEIGHT, allow for the stacked frames: ## 1 char high for the '.fRbuttons' frame, ## 1 char high for the '.fRinputs1' frame, ## 2 chars high for the '.fRinputs2' frame, ## 1 char high for the '.fRinputs3' frame, ## 1 char high for the '.fRmsg' frame, ## 1 char high for the '.fRresult' frame, ## IMPLEMENT THESE PLOT FRAMES LATER: ## 2 chars high for the '.fRplotcntl' frame, ## at least 50 pixels high for the '.fRplot' frame. ##+##################################################################### ## FOR WIDTH: set minWidthPx [font measure fontTEMP_varwidth \ " $aRtext(buttonPLOT) Color Color Color Color "] ## We add some pixels to account for right-left-size of ## window-manager decoration (~8 pixels) and some pixels for ## frame/widget borders (5 widgets x 4 pixels/widget = 20 pixels). set minWinWidthPx [expr {28 + $minWidthPx}] ## For HEIGHT --- for ## 1 char high for the '.fRbuttons' frame, ## 1 char high for the '.fRinputs1' frame, ## 2 chars high for the '.fRinputs2' frame, ## 1 char high for the '.fRinputs3' frame, ## 1 char high for the '.fRmsg' frame, ## 1 char high for the '.fRresult' frame, ## IMPLEMENT THESE PLOT FRAMES LATER: ## 2 chars high for the '.fRplotcntl' frame, ## 50 pixels high for the '.fRplot' frame. set charHeightPx [font metrics fontTEMP_SMALL_varwidth -linespace] set minWinHeightPx [expr {7 * $charHeightPx}] ## Add about 20 pixels for top-bottom window decoration -- ## and some pixels for top-and-bottom of frame/widget borders ## (6 frame-widgets x 4 pixels/widget = 24 pixels). set minWinHeightPx [expr {44 + $minWinHeightPx}] ## COMMENTED CALCS INCLUDING PLOT AREA. Implement later. if {0} { set minWinHeightPx [expr {10 * $charHeightPx}] ## Add about 50 pixels for height of the canvas ## AND add about 20 pixels for top-bottom window decoration -- ## and some pixels for top-and-bottom of frame/widget borders ## (7 frame-widgets x 4 pixels/widget = 28 pixels). set minWinHeightPx [expr {98 + $minWinHeightPx}] } ## END OF COMMENTED CALCS INCLUSDING PLOT AREA. Implement later. ## 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. ## ## Until the plot area (including canvas) is implemented, ## we use a fixed window size. wm resizable . 0 0 ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : '.fRbuttons' '.fRinputs1' '.fRinputs2' '.fRinputs3' ## '.fRmsg' '.fRresult' '.fRplotcntl' '.fRplot' ## ## Sub-frames: none ##+################################################################ ## 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 -bd $BDwidthPx_frame # frame .fRinputs1 -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRinputs1 -relief raised -bd 2 # frame .fRinputs2 -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRinputs2 -relief raised -bd 2 # frame .fRinputs3 -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRinputs3 -relief raised -bd 2 # frame .fRmsg -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRmsg -relief raised -bd 2 # frame .fRresult -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRresult -relief raised -bd 2 # frame .fRplotcntl -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRplotcntl -relief raised -bd 2 # frame .fRplot -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRplot -relief raised -bd 2 ##+############################## ## PACK the FRAMES. ##+############################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRinputs1 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRinputs2 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRinputs3 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRmsg \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRresult \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRplotcntl \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRplot \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+########################################################## ## The FRAMES ARE PACKED. START PACKING WIDGETS IN THE FRAMES. ##+########################################################## ##+########################################################## ## In FRAME '.fRbuttons' - ## DEFINE 'BUTTON' WIDGETS --- Exit, Help. ## Then PACK the widgets. ##+########################################################## button .fRbuttons.buttEXIT \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .fRbuttons.buttHELP \ -text "$aRtext(buttonHELP)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {popup_msgVarWithScroll .topHelp "$HELPtext" +150+50} button .fRbuttons.buttCALC \ -text "$aRtext(buttonCALC)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {calculate} label .fRbuttons.labelFORMULA \ -text "$aRtext(labelFORMULA)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Pack the widgets in frame '.fRbuttons'. pack .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ .fRbuttons.buttCALC \ .fRbuttons.labelFORMULA \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRinputs1' - ## DEFINE 1 LABEL widget, 1 ENTRY widget, and 1 LABEL widget. ## Then PACK THEM. ##+########################################################## label .fRinputs1.labelINITvalue \ -text "$aRtext(labelINITvalue)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label entry .fRinputs1.entryINITvalue \ -textvariable VARinitValue \ -width 8 \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -relief sunken \ -bd $BDwidthPx_entry label .fRinputs1.labelINITdesc \ -text "$aRtext(labelINITdesc)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Pack ALL widgets in the '.fRinputs1' frame. pack .fRinputs1.labelINITvalue \ .fRinputs1.entryINITvalue \ .fRinputs1.labelINITdesc \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRinputs2' - ## DEFINE 1 LABEL widget, 1 ENTRY widget, and 1 LABEL widget. ## Then PACK THEM. ##+########################################################## label .fRinputs2.labelRATEvalue \ -text "$aRtext(labelRATEvalue)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label entry .fRinputs2.entryRATEvalue \ -textvariable VARratePercent \ -width 8 \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -relief sunken \ -bd $BDwidthPx_entry label .fRinputs2.labelRATEdesc \ -text "$aRtext(labelRATEdesc)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## PACK the widgets in the '.fRinputs2' frame. pack .fRinputs2.labelRATEvalue \ .fRinputs2.entryRATEvalue \ .fRinputs2.labelRATEdesc \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRinputs3' - ## DEFINE 1 LABEL widget, 1 ENTRY widget, and 1 LABEL widget. ## Then PACK THEM. ##+########################################################## label .fRinputs3.labelNperiods \ -text "$aRtext(labelNperiods)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label entry .fRinputs3.entryNperiods \ -textvariable VARnumPeriods \ -width 8 \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -relief sunken \ -bd $BDwidthPx_entry label .fRinputs3.labelNdesc \ -text "$aRtext(labelNdesc)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## PACK the widgets in the '.fRinputs3' frame. pack .fRinputs3.labelNperiods \ .fRinputs3.entryNperiods \ .fRinputs3.labelNdesc \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRmsg' - ## DEFINE one LABEL widget. Then PACK it. ##+####################################################### label .fRmsg.labelMSG \ -text "" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bg "#ff9999" \ -bd $BDwidthPx_label ## Pack the widgets in frame '.fRmsg'. pack .fRmsg.labelMSG \ -side top \ -anchor nw \ -fill x \ -expand 0 ##+######################################################## ## In FRAME '.fRresult' - ## DEFINE one LABEL and one TEXT widget. Then PACK them. ##+####################################################### label .fRresult.labelRESULT \ -text "$aRtext(labelRESULT)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label text .fRresult.textRESULT \ -relief raised \ -borderwidth $BDwidthPx_text \ -height 1 \ -width 11 \ -wrap none \ -font fontTEMP_fixedwidth label .fRresult.labelRESULTdesc \ -text "$aRtext(labelRESULTdesc)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Pack the widgets in frame '.fRresult'. pack .fRresult.labelRESULT \ .fRresult.textRESULT \ .fRresult.labelRESULTdesc \ -side left \ -anchor nw \ -fill x \ -expand 0 ## COMMENTED PLOT FRAMES. Implement later. if {0} { ##+########################################################## ## In FRAME '.fRplotcntl' - ## DEFINE five 'BUTTON' WIDGETS ## --- a Plot button and 4 color buttons. ## Then PACK them. ##+########################################################## button .fRplotcntl.buttPLOT \ -text "$aRtext(buttonPLOT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {Redraw} button .fRplotcntl.buttCOLORbkgd \ -text "$aRtext(buttonCOLORbkgd)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_background_color" button .fRplotcntl.buttCOLORline \ -text "$aRtext(buttonCOLORline)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_line_color" button .fRplotcntl.buttCOLORtitle \ -text "$aRtext(buttonCOLORtitle)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_title-labels_color" button .fRplotcntl.buttCOLORticmarks \ -text "$aRtext(buttonCOLORticmarks)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_ticmarks_color" ## Pack the widgets in frame '.fRplotcntl'. pack .fRplotcntl.buttPLOT \ .fRplotcntl.buttCOLORbkgd \ .fRplotcntl.buttCOLORline \ .fRplotcntl.buttCOLORtitle \ .fRplotcntl.buttCOLORticmarks \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRplot' - ## DEFINE a CANVAS WIDGET (no scrollbars). Then PACK it. ## ## We highlightthickness & borderwidth of the canvas to ## zero, as suggested on page 558, Chapter 37, 'The Canvas ## Widget', in the 4th edition of the book 'Practical ## Programming in Tcl and Tk'. ##+####################################################### canvas .fRplot.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 ## Pack the widgets in frame '.fRplot'. pack .fRplot.can \ -side top \ -anchor nw \ -fill none \ -expand 0 } ## END OF COMMENTED PLOT FRAMES. Implement later. ##+################################################## ## END OF DEFINITION of the GUI widgets. ##+################################################## ## Start of BINDINGS, PROCS, Added-GUI-INIT sections. ##+################################################## ##+################################################################## ##+################################################################## ## BINDINGS SECTION: ##+################################################################## ## Clear the result field if it looks like the user is changing a parameter. bind .fRinputs1.entryINITvalue {.fRresult.textRESULT delete 1.0 end} bind .fRinputs2.entryRATEvalue {.fRresult.textRESULT delete 1.0 end} bind .fRinputs3.entryNperiods {.fRresult.textRESULT delete 1.0 end} ##+################################################################## ##+################################################################## ## PROCS SECTION: ## ## 'calculate' - Called by the 'Calculate' button. ## ## ********************* ## Drawing utility procs: ## ********************* ## ## 'setMappingVars_for_px2wc' - Called by the 'draw_plot' proc below. ## ## Set up constants to be used in converting ## a 'world coordinate' to a 'pixel coordinate' ## on the Tk canvas --- for the 'draw_plot' proc. ## ## This proc takes the coordinates of an UpperLeft (UL) ## point and a LowerRight (LR) point --- in BOTH ## 'world coordinates' and 'pixel coordinates' and ## sets some global variables to be used by the ## other drawing procs --- mainly the ratio: ## ## the number-of-pixels-per-world-coordinate-unit, ## global variable 'PXperWC' ## ## 'Xwc2px' - Converts an x world-coordinate to pixel units, ## for the following 'draw' procs. ## ## 'Ywc2px' - Converts a y world-coordinate to pixel units, ## for the following 'draw' procs. ## ## (Note: The following 'draw' procs take world-coordinates ## as input. The 'Xwc2px' and 'Ywc2px' procs are used ## by the 'draw' procs to calculate pixel coordinates for ## the Tk canvas 'create' commands.) ## ##***************************** ## End of Drawing utility procs. ##***************************** ## ##*************************** ## Color-Button utility procs: ##*************************** ## ## 'set_background_color' - Called by the background color button. ## ## 'set_line_color' - Called by the plot lines (and points) color button. ## ## 'set_title-labels_color' - Called by the plot title (and labels) color button. ## ## 'set_ticmarks_color' - Called by the ticmarks color button. ## ## 'update_button_colors' - Sets the color of the 4 color buttons. ## 'update_button_colors' - Sets the color of the 4 color buttons. ## ##********************************** ## End of Color-Button utility procs. ##********************************** ## ## 'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var. ## ##+################################################################# ##+######################################################################## ## PROC 'calculate' ##+######################################################################## ## PURPOSE: Calculates the growth (or decay) ## TOT = INIT*(1 + R/100)^N ## based on the INIT, R, N parameters from the GUI. ## ## CALLED BY: the 'Calculate' button ##+######################################################################## proc calculate {} { ## INPUT: global VARinitValue VARratePercent VARnumPeriods ## SHARED: global EDITcode ## OUTPUT: global TOToutput edit_inputs if {$EDITcode > 0} {return} set onePeriodInterest [expr {(1.0 + ($VARratePercent / 100.0))}] set TOTinterest [expr {pow( $onePeriodInterest , $VARnumPeriods)}] set TOToutput [expr {$VARinitValue * $TOTinterest}] .fRresult.textRESULT delete 1.0 end .fRresult.textRESULT insert end [ format "%7.4f" $TOToutput] } ## END OF PROC 'calculate' ##+#################################################################### ## PROC: 'edit_inputs' ##+##################################################################### ## PURPOSE: Checks entry widgets entries and pops up an error message ## if the data is invalid. ## ## CALLED BY: the 'distance_update' proc ##+##################################################################### proc edit_inputs {} { global VARinitValue VARratePercent VARnumPeriods ## We could do without the following EDITcode variable, by using ## a code with the 'return' statement herein. But using this ## code variable is a little more self-documenting. global EDITcode set EDITcode 0 ####################################################### ## Remove trailing and leading blanks (if any) from the ## user entries in the 'entry' widgets. ####################################################### set VARinitValue [string trim $VARinitValue] set VARratePercent [string trim $VARratePercent] set VARnumPeriods [string trim $VARnumPeriods] ######################################################################### ## Check that VARinitValue VARratePercent VARnumPeriods are NOT blank. ######################################################################### set MSGblank "is blank. Must NOT be blank." if {"$VARinitValue" == ""} { popup_msgVarWithScroll .topErr "The initial value (INIT) $MSGblank" +10+10 set EDITcode 1 return } if {"$VARratePercent" == ""} { popup_msgVarWithScroll .topErr "The Rate Percent (R) $MSGblank" +10+10 set EDITcode 1 return } if {"$VARnumPeriods" == ""} { popup_msgVarWithScroll .topErr "The Number of Periods (N) $MSGblank" +10+10 set EDITcode 1 return } ########################################################## ## Check that VARnumPeriods is an integer. ########################################################## set MSGnotInteger " is NOT INTEGER." if {![string is integer -strict "$VARnumPeriods"]} { popup_msgVarWithScroll .topErr "Number of Periods (N) $MSGnotInteger" +10+10 set EDITcode 1 return } ######################################################################### ## Check that VARinitValue VARratePercent are decimal numbers ## (positive or negative) --- such as ## 1.234 or -3 or -3.0 or -.4 or .5 or 7 ######################################################################### ## Implemented using the 'decimal_check' proc below. ######################################################################### set NUMERICmsg "should be a decimal number. Examples: 1.234 or 0.56 or -.789" if {![decimal_check "$VARinitValue"]} { popup_msgVarWithScroll .topErr "Initial value (INIT) $NUMERICmsg" +10+10 set EDITcode 1 return } if {![decimal_check "$VARratePercent"]} { popup_msgVarWithScroll .topErr "Rate Percent (R) $NUMERICmsg" +10+10 set EDITcode 1 return } } ## 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 'setMappingVars_for_px2wc' ##+######################################################################## ## PURPOSE: Sets up 'constants' to be used in converting between x,y ## 'world coordinates' and 'pixel coordinates' on a Tk canvas. ## ## Puts the constants in global variables: ## PXperWC BASEwcX BASEwcY BASEpxX BASEpxY ## ## These variables are for use by the 'Xwc2px' and 'Ywc2px' procs. ## ## The 'BASE' variables are coordinates of the upper-left point ## of the 'plotting rectangle' --- in world coordinates and ## in pixel coordinates. ## ## METHOD: This proc takes the coordinates of an UpperLeft (UL) ## point and a LowerRight (LR) point --- in both ## 'world coordinates' and 'pixel coordinates' and ## sets some global variables to the used by the ## other drawing procs --- mainly the ratio: ## ## the number-of-pixels-per-world-coordinate-unit, ## in global variable 'PXperWC' ## ## (This will generally include a fractional amount, ## i.e. it is not necessarily an integer.) ## ## INPUTS: ## ## At least eight numbers are input to this proc, as indicated by: ## ## ULwcX ULwcY ULpxX ULpxY LRwcX LRwcY LRpxX LRpxY ## ## Generally, the 'wc' inputs may be floating point numbers, and the ## 'px' inputs will generally be (non-negative) integers. ## ## Example: ## setMappingVars_for_px2wc xy -1.2 1.2 0 0 1.2 -0.2 $canvasWidthPx $canvasHeightPx ## ## The first argument can be either 'x' or 'y' or 'xy'. This determines whether ## global variable 'PXperWC' is detemined by just the X-numbers, just the Y-numbers, ## or both. In this script, we use 'xy' (both). ## ## An 'adjustYpx' global variable can be used to adjust if the pixels ## on a user's monitor are not square. ## ## OUTPUTS: global variables PXperWC BASEwcX BASEwcY BASEpxX BASEpxY ## ## CALLED BY: by the Redraw' proc. ##+######################################################################## proc setMappingVars_for_px2wc {xORy ULwcX ULwcY ULpxX ULpxY LRwcX LRwcY LRpxX LRpxY} { global PXperWC BASEwcX BASEwcY BASEpxX BASEpxY adjustYpx ## FOR TESTING: (to dummy out this proc) # return ############################################################ ## Calculate PXperWC (pixels-per-world-coordinate-unit) by ## first calculating PXperWCx and PXperWCy --- the ratio ## of pixels-per-world-coordinate in the x and y directions, ## for the given UL and LR values. ############################################################ set PXperWCx [expr {abs(($LRpxX - $ULpxX) / ($LRwcX - $ULwcX))}] set PXperWCy [expr {abs(($LRpxY - $ULpxY) / ($LRwcY - $ULwcY))}] ## FOR TESTING: if {0} { puts "proc 'Redraw':" puts "LRwcY: $LRwcY ULwcY: $ULwcY LRwcX: $LRwcX ULwcX: $ULwcX" puts "PXperWCx: $PXperWCx" puts "LRpxY: $LRpxY ULpxY: $ULpxY LRpxX: $LRpxX ULpxX: $ULpxX" puts "PXperWCy: $PXperWCy" } ############################################################# ## Set PXperWC according to whether input variable 'xORy' ## is 'x' or 'y' or other (such as 'xy'). ############################################################# if {$xORy == "x"} { set PXperWC $PXperWCx } elseif {$xORy == "y"} { set PXperWC $PXperWCy } else { if {$PXperWCx > $PXperWCy} { set PXperWC $PXperWCy } else { set PXperWC $PXperWCx } } ## END OF if {$xORy == "x"} ############################################################ ## In case the pixels are not square, provide a factor ## that can be used to adjust in the Y direction. ############################################################ set adjustYpx 1.0 ############################################################ ## Set BASEwcX, BASEwcY, BASEpxX and BASEpxY. ############################################################ set BASEwcX $ULwcX set BASEwcY $ULwcY set BASEpxX $ULpxX set BASEpxY $ULpxY ## FOR TESTING: if {0} { puts "proc 'setMappingVars_for_px2wc':" puts "PXperWC: $PXperWC" puts "BASEwcX: $BASEwcX BASEwcY: $BASEwcY" puts "BASEpxX: $BASEpxX BASEpxY: $BASEpxY" } } ## END OF PROC 'setMappingVars_for_px2wc' ##+######################################################################## ## PROC 'Xwc2px' ##+######################################################################## ## PURPOSE: Converts an x world-coordinate to pixel units. ## ## CALLED BY: the 'draw' procs ##+######################################################################## proc Xwc2px {x} { global PXperWC BASEwcX BASEwcY BASEpxX BASEpxY adjustYpx set px [expr {($x - $BASEwcX) * $PXperWC + $BASEpxX}] return $px } ## END OF PROC 'Xwc2px' ##+######################################################################## ## PROC 'Ywc2px' ##+######################################################################## ## PURPOSE: Converts an y world-coordinate to pixel units. ## ## CALLED BY: the 'draw' procs ##+######################################################################## proc Ywc2px {y} { global PXperWC BASEwcX BASEwcY BASEpxX BASEpxY adjustYpx set px [expr {($BASEwcY - $y) * $PXperWC * $adjustYpx + $BASEpxY}] return $px } ## END OF PROC 'Ywc2px' ##+######################################################################## ## PROC 'draw_plot' ##+######################################################################## ## PURPOSE: Draws the plot of (1 + 1/n)^n on the canvas. ## ## CALLED BY: the 'Plot' button ##+####################################################################### proc draw_plot {} { ## FOR TESTING: (to dummy out this proc) return global COLORBKGDhex COLORLINEhex COLORTITLEhex COLORTICShex ############################################### ## Convert world-coords to pixels. ############################################### set x1Px [Xwc2px $x1] set y1Px [Ywc2px $y1] set x2Px [Xwc2px $x2] set y2Px [Ywc2px $y2] ## FOR TESTING: if {0} { puts "proc 'draw_line_x1y1x2y2':" puts "x1: $x1 y1: $y1 x1Px: $x1Px y1Px: $y1Px" puts "x2: $x2 y2: $y2 x2Px: $x2Px y2Px: $y2Px" } ##################################### ## Draw the line. ##################################### .fRplot.can create line \ $x1Px $y1Px $x2Px $y2Px \ -fill $hexRGB -width $lineWidthPx -dash $dashPattern -tags $tagID ############################################### ## Convert world-coords to pixels. ############################################### set x1Px [Xwc2px $x1] set y1Px [Ywc2px $y1] ## FOR TESTING: if {0} { puts "proc 'draw_pointOval_x1y1_radiusPx':" puts "x1: $x1 y1: $y1 x1Px: $x1Px y1Px: $y1Px" } ################################################################## ## Set the corner coords for the (small) circle. ################################################################## set topleftXpx [expr {$x1Px - $radiusPx}] set topleftYpx [expr {$y1Px - $radiusPx}] set botrightXpx [expr {$x1Px + $radiusPx}] set botrightYpx [expr {$y1Px + $radiusPx}] ################################################ ## Draw a circle at x1,y1. ################################################ .fRplot.can create oval \ $topleftXpx $topleftYpx $botrightXpx $botrightYpx \ -fill $hexRGB -width $lineWidthPx -outline {} -tag $tagID ## If we want an outline around the circle, use something like this: # -width $lineWidthPx -outline $drawRGB ############################################### ## Convert x1,y1 world-coord units to pixels. ############################################### set x1Px [Xwc2px $x1] set y1Px [Ywc2px $y1] ## FOR TESTING: if {0} { puts "proc 'draw_text_x1y1_center':" puts "x1: $x1 y1: $y1 x1Px: $x1Px y1Px: $y1Px" } ###################################### ## Draw the text string. ###################################### .fRplot.can create text \ $x1Px $y1Px \ -anchor center -justify center -fill $hexRGB \ -text $textSTRING -font fontTEMP_SMALL_varwidth -tag $tagID } ## END OF PROC 'draw_plot' ##+##################################################################### ## PROC 'set_background_color' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the (background) color of the canvas --- ## on which all the geometry is to be drawn. ## ## Arguments: none ## ## CALLED BY: .fRplotcntl.buttCOLORbkgd button ##+##################################################################### proc set_background_color {} { global COLORBKGDr COLORBKGDg COLORBKGDb COLORBKGDhex ColorSelectorScript ## FOR TESTING: # puts "COLORBKGDr: $COLORBKGDr" # puts "COLORBKGDg: $COLORBKGDb" # puts "COLORBKGDb: $COLORBKGDb" set TEMPrgb [ exec $ColorSelectorScript $COLORBKGDr $COLORBKGDg $COLORBKGDb] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLORBKGDhex "#$hexRGB" set COLORBKGDr $r255 set COLORBKGDg $g255 set COLORBKGDb $b255 ## Set color of background-color button. update_button_colors ## Set the color of the canvas. .fRplot.can config -bg $COLORBKGDhex } ## END OF PROC 'set_background_color' #+##################################################################### ## PROC 'set_line_color' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the color of the lines (and ## data points) to be drawn on the canvas. ## ## Arguments: none (global variables are used) ## ## CALLED BY: .fRplotcntl.buttCOLORline button ##+##################################################################### proc set_line_color {} { global COLORLINEr COLORLINEg COLORLINEb COLORLINEhex ColorSelectorScript ## FOR TESTING: # puts "COLORLINEr: $COLORLINEr" # puts "COLORLINEg: $COLORLINEb" # puts "COLORLINEb: $COLORLINEb" set TEMPrgb [ exec $ColorSelectorScript $COLORLINEr $COLORLINEg $COLORLINEb] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLORLINEhex "#$hexRGB" set COLORLINEr $r255 set COLORLINEg $g255 set COLORLINEb $b255 ## Set color of the draw-color button. update_button_colors ## Redraw the plot. draw_plot } ## END OF PROC 'set_line_color' #+##################################################################### ## PROC 'set_title_color' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the color of the title (and labels) ## drawn on the canvas. ## ## Arguments: none (global variables are used) ## ## CALLED BY: .fRplotcntl.buttCOLORtitle button ##+##################################################################### proc set_title_color {} { global COLORTITLEr COLORTITLEg COLORTITLEb COLORTITLEhex ColorSelectorScript ## FOR TESTING: # puts "COLORTITLEr: $COLORTITLEr" # puts "COLORTITLEg: $COLORTITLEb" # puts "COLORTITLEb: $COLORTITLEb" set TEMPrgb [ exec $ColorSelectorScript $COLORTITLEr $COLORTITLEg $COLORTITLEb] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLORTITLEhex "#$hexRGB" set COLORTITLEr $r255 set COLORTITLEg $g255 set COLORTITLEb $b255 ## Set color of the draw-color button. update_button_colors ## Redraw the plot. draw_plot } ## END OF PROC 'set_title_color' #+##################################################################### ## PROC 'set_ticmarks_color' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the color of the ticmarks ## drawn on the canvas. ## ## Arguments: none (global variables are used) ## ## CALLED BY: .fRplotcntl.buttCOLORticmarks button ##+##################################################################### proc set_ticmarks_color {} { global COLORTICSr COLORTICSg COLORTICSb COLORTICShex ColorSelectorScript ## FOR TESTING: # puts "COLORTICSr: $COLORTICSr" # puts "COLORTICSg: $COLORTICSb" # puts "COLORTICSb: $COLORTICSb" set TEMPrgb [ exec $ColorSelectorScript $COLORTICSr $COLORTICSg $COLORTICSb] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLORTICShex "#$hexRGB" set COLORTICSr $r255 set COLORTICSg $g255 set COLORTICSb $b255 ## Set color of the draw-color button. update_button_colors ## Redraw the circle. draw_circle } ## END OF PROC 'set_ticmarks_color' ##+##################################################################### ## PROC 'update_button_colors' ##+##################################################################### ## PURPOSE: ## This procedure is invoked to set the background color of each of ## 4 color buttons to its current color --- and sets foreground color, ## for text on the 4 buttons, to a suitable black or white color, ## so that the label text is readable. ## ## Arguments: global color vars ## ## CALLED BY: 4 colors procs: ## 'set_background_color' 'set_line_color' ## 'set_title_color' 'set_ticmarks_color' ## and in the additional-GUI-initialization section at ## the bottom of this script to initialize the color of ## the buttons. ##+##################################################################### proc update_button_colors {} { global COLORBKGDr COLORBKGDg COLORBKGDb COLORBKGDhex \ COLORLINEr COLORLINEg COLORLINEb COLORLINEhex \ COLORTITLEr COLORTITLEg COLORTITLEb COLORTITLEhex \ COLORTICSr COLORTICSg COLORTICSb COLORTICShex # set colorBREAK 300 set colorBREAK 375 .fRplotcntl.buttCOLORbkgd configure -bg $COLORBKGDhex set sumCOLOR [expr {$COLORBKGDr + $COLORBKGDg + $COLORBKGDb}] if {$sumCOLOR > $colorBREAK} { .fRplotcntl.buttCOLORbkgd configure -fg "#000000" } else { .fRplotcntl.buttCOLORbkgd configure -fg "#f0f0f0" } .fRplotcntl.buttCOLORline configure -bg $COLORLINEhex set sumCOLOR [expr {$COLORLINEr + $COLORLINEg + $COLORLINEb}] if {$sumCOLOR > $colorBREAK} { .fRplotcntl.buttCOLORline configure -fg "#000000" } else { .fRplotcntl.buttCOLORline configure -fg "#f0f0f0" } .fRplotcntl.buttCOLORtitle configure -bg $COLORTITLEhex set sumCOLOR [expr {$COLORTITLEr + $COLORTITLEg + $COLORTITLEb}] if {$sumCOLOR > $colorBREAK} { .fRplotcntl.buttCOLORtitle configure -fg "#000000" } else { .fRplotcntl.buttCOLORtitle configure -fg "#f0f0f0" } .fRplotcntl.buttCOLORticmarks configure -bg $COLORTICShex set sumCOLOR [expr {$COLORTICSr + $COLORTICSg + $COLORTICSb}] if {$sumCOLOR > $colorBREAK} { .fRplotcntl.buttCOLORticmarks configure -fg "#000000" } else { .fRplotcntl.buttCOLORticmarks configure -fg "#f0f0f0" } } ## END OF PROC 'update_button_colors' ##+##################################################################### ## PROC 'advise_user' ##+##################################################################### ## PURPOSE: Puts a message to the user on the GUI. ## ## CALLED BY: in the additional-GUI-initialization section at ## the bottom of this script ## and in some procs. ##+##################################################################### 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_varwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" 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_varwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 } 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 } ##################################### ## 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 PROC definitions. ##+######################## ##+######################## ## Set the 'HELPtext' var. ##+######################## set HELPtext "\ \ \ ** HELP for this 'Compounded Growth (or decline) Computation' App ** This Tk script is a calculator that allows one to easily compute compounded growth --- for example, - in a bank account with a fixed interest rate, or - in a population for a fixed net growth rate --- for example, human population growth, or - in a population for a fixed net decline rate (negative growth) --- for example, decline of rhinos, lions, tigers, giraffes, whales, polar-bears, and other large, non-human-food, non-tax-paying mammals. (Unfortunately, because they are not tax-payers, who are counted in periodic censuses, good rate of decline data is hard to find.) This calculator computes a result based on several inputs: - INIT, an initial value (such a bank balance or population) - R, a growth (or decline) rate (per time period, such as per year) - N, the number of time periods, the end of which is the time point at which the result, TOT, is calculated. The calculation is summarized by TOT = INIT * (1 + R/100)^N ******************************* METHOD USED to perform the plot: ******************************* (NOTE: Plot frames --- for plot parameters and for a graphics area --- are not implemented yet. A 'canvas' is to be added to the bottom of the GUI. In 'world coordinates' (not pixel coordinates), the x-axis for this plot goes from 0.0 to about N (time periods) --- with integer-time-period values plotted along the x-axis. The y-axis goes from 0.0 to about the max of INIT and TOT --- call this MAX. (We assume here that INIT is positive.) A utility proc is used to 'apply' 'world coordinates' to the pixel coordinates of the Tk canvas. The corners of the plot area in 'world coordinates' are mapped to the corners of the plot area in 'pixel coordinates'. To allow some margin around the rectangular plot area, the world coordinates of the corners of the rectangular canvas plot area will be (approximately) - top left (-0.1*N , +1.1*MAX) - bottom right (+1.1*N , -0.1*MAX) Two procs -- 'Xwc2px' and 'Ywc2px' -- are used to convert world coordinates to pixel coordinates that are to be used in the 'create line', 'create text', and 'create oval' commands that are used to draw the plot on the Tk canvas. ************************** WINDOW (and canvas) RESIZE: ************************** If/when the plot frames are implemented: We allow the user to resize the window rather than using a fixed window (and fixed canvas/plot-area) size. If the user resizes the window, the 'Plot' button can be used to force the plot to be redrawn according to the new window (and canvas) size. ******************************** CAPTURING THE GUI IMAGE: ******************************** A screen/window capture utility (like 'gnome-screenshot' on Linux) can be used to capture the GUI image in a PNG file, say. If necessary, an image editor (like 'mtpaint' on Linux) can be used to crop the window capture image. The image could also be down-sized --- say to make a smaller image suitable for use in a web page or an email. " ##+################################################################ ##+################################################################ ## ADDITIONAL GUI INITIALIZATION SECTION: Mainly to ## - Draw an initial circle and polygon(s) on the canvas. ##+################################################################ ##+################################################################ ## COMMENTED PLOT AREA INITIALIZATION. Implement later. if {0} { ##+##################################################### ## Set the full-name of the RGB color-selector Tk script ## that is used in several procs above. ##+##################################################### ## FOR TESTING: # puts "argv0: $argv0" set DIRthisScript "[file dirname $argv0]" ## For ease of testing in a Linux/Unix terminal and located at the ## directory containing this Tk script. Set the full directory name. if {"$DIRthisScript" == "."} { set DIRthisScript "[pwd]" } set DIRupOne "[file dirname "$DIRthisScript"]" set DIRupTwo "[file dirname "$DIRupOne"]" set ColorSelectorScript "$DIRupTwo/SELECTORtools/tkRGBselector/sho_colorvals_via_sliders3rgb.tk" ## Alternatively: Put the RGB color-selector Tk script in the ## same directory as this Tk script and uncomment the following. # set ColorSelectorScript "$DIRthisScript/sho_colorvals_via_sliders3rgb.tk" ##+############################################################ ## Initialize the 4 drawing colors --- for background, line, ## title-and-labels, and ticmarks. ##+############################################################ set COLORBKGDr 90 set COLORBKGDg 90 set COLORBKGDb 255 set COLORBKGDhex [format "#%02X%02X%02X" $COLORBKGDr $COLORBKGDg $COLORBKGDb] set COLORLINEr 255 set COLORLINEg 255 set COLORLINEb 255 set COLORLINEhex [format "#%02X%02X%02X" $COLORLINEr $COLORLINEg $COLORLINEb] set COLORTITLEr 255 set COLORTITLEg 0 set COLORTITLEb 0 set COLORTITLEhex [format "#%02X%02X%02X" $COLORTITLEr $COLORTITLEg $COLORTITLEb] set COLORTICSr 0 set COLORTICSg 255 set COLORTICSb 0 set COLORTICShex [format "#%02X%02X%02X" $COLORTICSr $COLORTICSg $COLORTICSb] ##+################################### ## Set the color of the color buttons. ##+################################### update_button_colors ##+############################################################ ## We use the following command because the 'draw_plot' proc ## may not (re)set the background/canvas color. ## Only the background-color button-proc sets the canvas color. ##+############################################################ .fRplot.can config -bg $COLORBKGDhex ##+################################################ ## Set a width for lines. ##+################################################ # set lineWidthPx 1 set lineWidthPx 2 } ## END OF COMMENTED PLOT AREA INITIALIZATION. Implement later. ##+################################################### ## Initialize the entry widgets variables. ##+################################################### ## Estimate growth of 7 billion people in 25 years ## at a 3% growth rate. Answer: 14.65 billion people set VARinitValue "7.0" set VARratePercent "3.0" set VARnumPeriods "25" ##+################################################### ## Calculate the result from these inital values. ##+################################################### calculate advise_user \ "** Click the 'Calculate' button after setting the 3 entry field parameters. **" ##+################################################# ## Draw plot, for the current canvas (window) size. ##+################################################# ## Need 'update' here to set the size of the canvas, ## because 'draw_plot' uses 'winfo' to get ## the width and height of the canvas --- ## to support resizing the drawing if the window ## is resized. ##+################################################# # update # draw_plot