#!/usr/bin/wish -f ##+######################################################################## ## ## SCRIPT: tkLendersCompoundInterestDreamCrushed_eIsTheLimit.tk ## ## PURPOSE: This Tk script is meant to demonstrate that there is a limit ## to the amount of interest that can be accumulated in a year ## of compounding --- no matter whether the interest is compounded ## annually, monthly, weekly, daily, hourly, every minute, ## or every second. ## ## In particular, this 'app' demonstrates that the ## sequence (1 + 1/n)^n approaches 2.71828... ## (dubbed the 'natural' number 'e'). ## ## The sequence (1 + 1/n)^n corresponds to a lender who wants ## 100% interest per year with options for various compounding rates. ## Say he/she wants to know how much more he.she could get ## by compounding the 100% interest annually, monthly, weekly, ## daily, hourly, every minute, or every second. ## ## For that lender, this 'app' can calculate the sequence ## (1 + 1)^1, (1 + 1/12)^12, (1 + 1/52)^52, (1 + 1/365)^365 (daily), ## (1 + 1/8760)^8760 where 8760 = 365*24 (hourly), and ## (1 + 1/525600)^525600 where 525600 = 8760*60 (by minute), and ## (1 + 1/31536000)^31536000 where 31546000 = 525600*60 (by second). ## ##+######################### ## PLANNED LAYOUT OF THE GUI: ## ## FrameNames ## VVVVVVVVVV ## ----------------------------------------------------------------------------- ## tkLendersCompoundInterestDreamCrushed ('e' --- about 272% --- Is The Limit) ## [window title] ## ----------------------------------------------------------------------------- ## ## .fRbuttons {Exit} {Help} ## ## .fRcontrols1 Desired 'base' ## annual interest (%): 100.00__ [This figure is shown in a 'disabled' entry widget.] ## ## .fRcontrols2 Compounding ## period: O annual O monthly O weekly O daily O hourly O by minute O by second ## ## .fRmsg [... Messages to the user can be displayed here on a label widget ...] ## ## .fRresult Actual annual ## interest (%): [ Calculated interest is shown in a text widget here. ] ## ## .fRplotcntl {Plot value of {Backgd {Line {Title-Labels {TicMarks ## (1 + 1/n)^n} Color} Color} Color} Color} ## ## .fRplot ----------------------------------------------------------------------------- ## | | ## | | ## | | ## | 'Canvas' for displaying a plot of (1 + 1/n)^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: ## ## 7 'button' widgets ## 4 'label' widgets ## 1 'entry' widget ## 1 'text' widget ## 7 'radiobutton' widgets ## 1 'canvas' widget (with x-y scrollbars) ## 0 'checkbutton' 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 32 million ## --- with integer-exponent values plotted along the x-axis. ## The y-axis goes from 0.0 to 300.0 (percent). ## ## 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 ## - top left (-1 million,+400.0) ## - bottom right (+32 million,-100.0) ## ## 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' buttons. ## ## '.fRcontrols1' - to contain a label and entry widget pair. ## ## '.fRcontrols2' - to contain a label widget and 7 radiobutton widgets. ## ## '.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: such as button1-release on the 'radiobutton' widgets ## ## 4) Define PROCS: ## ## 'calc_interest' - Called by button1-release bindings on the ## compounding-period radiobuttons. ## ##**************************** ## 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 group of radiobuttons. ## ##+####################################################################### ## 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 . "tkLendersCompoundInterestDreamCrushed ('e' --- about 272% --- Is The Limit)" wm iconname . "tkLenderCompounding" wm geometry . +8+30 ##+###################################################### ## Set the COLOR SCHEME for the window and its widgets --- ## such as radiobutton and entry field background color. ##+###################################################### tk_setPalette "#f0f0f0" set radbuttBKGD "#c0c0c0" set entryBKGD "#ffffff" # set scaleBKGD "#f0f0f0" # 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" ## RADIOBUTTON geom parameters: set PADXpx_radbutt 0 set PADYpx_radbutt 0 set BDwidthPx_radbutt 1 set RELIEF_radbutt_hi "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" ## 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" ## For the '.fRcontrols1' frame: set aRtext(labelBASEinterest) "Uncompounded 'base' annual interest (%):" set aRtext(labelBASEdesc) "(a usurious rate, admittedly)" ## For the '.fRcontrols2' frame: set aRtext(labelCOMPOUNDINGperiod) "Compounding period (exponent, N):" set aRtext(radbuttANNUAL) "annual N=1" set aRtext(radbuttMONTHLY) "monthly N=12" set aRtext(radbuttWEEKLY) "weekly N=52" set aRtext(radbuttDAILY) "daily N=365" set aRtext(radbuttHOURLY) "hourly N=8760" set aRtext(radbuttBYminute) "by minute N=525600" set aRtext(radbuttBYsecond) "by second N=31536000" ## For the '.fRresult' frame: set aRtext(labelRESULT) "RESULT - Actual annual interest (%):" set aRtext(labelRESULTdesc) " (including the principal)" ## For the '.fRplotcntl' frame: set aRtext(buttonPLOT) "PLOT graph of (1 + 1/n)^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, ## 2 chars high for the '.fRcontrols1' frame, ## 2 chars high for the '.fRcontrols2' frame, ## 1 char high for the '.fRmsg' frame, ## 2 chars high for the '.fRresult' frame, ## 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 '.fRcontrols1' frame, ## 2 chars high for the '.fRcontrols2' frame, ## 1 char high for the '.fRmsg' frame, ## 1 char high for the '.fRresult' frame, ## IMPLEMENT PLOT AREA 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 {6 * $charHeightPx}] ## Add about 20 pixels for top-bottom window decoration -- ## and some pixels for top-and-bottom of frame/widget borders ## (5 frame-widgets x 4 pixels/widget = 20 pixels). set minWinHeightPx [expr {40 + $minWinHeightPx}] ## COMMENT OUT MIN HEIGHT SETTING INCLUDING PLOT FRAMES. ## 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 ## (6 frame-widgets x 4 pixels/widget = 24 pixels). set minWinHeightPx [expr {94 + $minWinHeightPx}] } ## END OF SETTING INCLUDING PLOT FRAMES. ## 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. ## ## With the plot frames commented out (no canvas), ## we freeze the size. wm resizable . 0 0 ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : '.fRbuttons' '.fRcontrols1' '.fRcontrols2' ## '.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 .fRcontrols1 -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRcontrols1 -relief raised -bd 2 # frame .fRcontrols2 -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRcontrols2 -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 ## COMMENT OUT PLOT AREA FRAMES. Implement later. if {0} { # 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 } ## END OF COMMENTED PLOT FRAMES. ##+############################## ## PACK the FRAMES. ##+############################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcontrols1 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcontrols2 \ -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 ## COMMENT OUT PACKING OF PLOT AREA FRAMES. Implement later. if {0} { pack .fRplotcntl \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRplot \ -side top \ -anchor nw \ -fill both \ -expand 1 } ## END OF COMMENTED PACKING OF PLOT FRAMES. ##+########################################################## ## 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} ## Pack the widgets in frame '.fRbuttons'. pack .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRcontrols1' - ## DEFINE 1 LABEL widget and 1 ENTRY widget. ## Then PACK THEM. ##+########################################################## label .fRcontrols1.labelBASEinterest \ -text "$aRtext(labelBASEinterest)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label entry .fRcontrols1.entryBASEinterest \ -textvariable VARbaseInterest \ -width 8 \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -relief sunken \ -bd $BDwidthPx_entry .fRcontrols1.entryBASEinterest configure -state disabled .fRcontrols1.entryBASEinterest configure -disabledforeground "#000000" label .fRcontrols1.labelBASEdesc \ -text "$aRtext(labelBASEdesc)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Pack ALL widgets in the '.fRcontrols1' frame. pack .fRcontrols1.labelBASEinterest \ .fRcontrols1.entryBASEinterest \ .fRcontrols1.labelBASEdesc \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRcontrols2' - ## DEFINE one LABEL widget and 7 RADIOBUTTON widgets. ## Then PACK THEM. ##+########################################################## label .fRcontrols2.labelCOMPOUNDINGperiod \ -text "$aRtext(labelCOMPOUNDINGperiod)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set VARcompoundingPeriod "monthly" radiobutton .fRcontrols2.radbuttANNUAL \ -text "$aRtext(radbuttANNUAL)" \ -font fontTEMP_SMALL_varwidth \ -anchor w \ -variable VARcompoundingPeriod \ -value "annual" \ -selectcolor "$radbuttBKGD" \ -relief $RELIEF_radbutt_hi \ -bd $BDwidthPx_radbutt radiobutton .fRcontrols2.radbuttMONTHLY \ -text "$aRtext(radbuttMONTHLY)" \ -font fontTEMP_SMALL_varwidth \ -anchor w \ -variable VARcompoundingPeriod \ -value "monthly" \ -selectcolor "$radbuttBKGD" \ -relief $RELIEF_radbutt_hi \ -bd $BDwidthPx_radbutt radiobutton .fRcontrols2.radbuttWEEKLY \ -text "$aRtext(radbuttWEEKLY)" \ -font fontTEMP_SMALL_varwidth \ -anchor w \ -variable VARcompoundingPeriod \ -value "weekly" \ -selectcolor "$radbuttBKGD" \ -relief $RELIEF_radbutt_hi \ -bd $BDwidthPx_radbutt radiobutton .fRcontrols2.radbuttDAILY \ -text "$aRtext(radbuttDAILY)" \ -font fontTEMP_SMALL_varwidth \ -anchor w \ -variable VARcompoundingPeriod \ -value "daily" \ -selectcolor "$radbuttBKGD" \ -relief $RELIEF_radbutt_hi \ -bd $BDwidthPx_radbutt radiobutton .fRcontrols2.radbuttHOURLY \ -text "$aRtext(radbuttHOURLY)" \ -font fontTEMP_SMALL_varwidth \ -anchor w \ -variable VARcompoundingPeriod \ -value "hourly" \ -selectcolor "$radbuttBKGD" \ -relief $RELIEF_radbutt_hi \ -bd $BDwidthPx_radbutt radiobutton .fRcontrols2.radbuttBYminute \ -text "$aRtext(radbuttBYminute)" \ -font fontTEMP_SMALL_varwidth \ -anchor w \ -variable VARcompoundingPeriod \ -value "minute" \ -selectcolor "$radbuttBKGD" \ -relief $RELIEF_radbutt_hi \ -bd $BDwidthPx_radbutt radiobutton .fRcontrols2.radbuttBYsecond \ -text "$aRtext(radbuttBYsecond)" \ -font fontTEMP_SMALL_varwidth \ -anchor w \ -variable VARcompoundingPeriod \ -value "second" \ -selectcolor "$radbuttBKGD" \ -relief $RELIEF_radbutt_hi \ -bd $BDwidthPx_radbutt ## PACK the widgets in the '.fRcontrols2' frame. pack .fRcontrols2.labelCOMPOUNDINGperiod \ .fRcontrols2.radbuttANNUAL \ .fRcontrols2.radbuttMONTHLY \ .fRcontrols2.radbuttWEEKLY \ .fRcontrols2.radbuttDAILY \ .fRcontrols2.radbuttHOURLY \ .fRcontrols2.radbuttBYminute \ .fRcontrols2.radbuttBYsecond \ -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 left \ -anchor nw \ -fill x \ -expand 1 ##+######################################################## ## 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 ## COMMENT the 2 PLOT FRAMES. Implement later. if {0} { ##+########################################################## ## In FRAME '.fRplotcntl' - ## DEFINE-and-PACK 'BUTTON' WIDGETS ## --- a Plot button and 4 color buttons. ##+########################################################## 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: ##+################################################################## bind .fRcontrols2.radbuttANNUAL {calc_interest} bind .fRcontrols2.radbuttMONTHLY {calc_interest} bind .fRcontrols2.radbuttWEEKLY {calc_interest} bind .fRcontrols2.radbuttDAILY {calc_interest} bind .fRcontrols2.radbuttHOURLY {calc_interest} bind .fRcontrols2.radbuttBYminute {calc_interest} bind .fRcontrols2.radbuttBYsecond {calc_interest} ##+################################################################## ##+################################################################## ## PROCS SECTION: ## ## 'calc_interest' - Called by button1-release bindings on the ## compounding-period radiobuttons. ## ## ********************* ## 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 'calc_interest' ##+######################################################################## ## PURPOSE: Calculates the 'actual' annual interest ## based on the compounding-period chosen via a click ## on a compounding-period radiobutton. ## ## Calculates the value of (1 + 1/n)^n ## where n comes from a user-selected 'compounding-period' --- ## annually, monthly, weekly, daily, hourly, every minute, or ## every second. ## ## The expressions to be evaluated (and n) are ## (1 + 1)^1, (1 + 1/12)^12, (1 + 1/52)^52, (1 + 1/365)^365 (daily), ## (1 + 1/8760)^8760 where 8760 = 365*24 (hourly), and ## (1 + 1/525600)^525600 where 525600 = 8760*60 (by minute), and ## (1 + 1/31536000)^31536000 where 31546000 = 525600*60 (by second). ## ## CALLED BY: by bindings on the compounding-period radiobuttons ##+######################################################################## proc calc_interest {} { ## INPUT: global VARcompoundingPeriod ## OUTPUT: global ActualInterest if {"$VARcompoundingPeriod" =="annual"} { set N 1 } if {"$VARcompoundingPeriod" =="monthly"} { set N 12 } if {"$VARcompoundingPeriod" =="weekly"} { set N 52 } if {"$VARcompoundingPeriod" =="daily"} { set N 365 } if {"$VARcompoundingPeriod" =="hourly"} { set N 8760 } if {"$VARcompoundingPeriod" =="minute"} { set N 525600 } if {"$VARcompoundingPeriod" =="second"} { set N 31536000 } set onePeriodInterest [expr {(1.0 + (1.0 / $N))}] set ActualInterest [expr {pow( $onePeriodInterest , $N)}] .fRresult.textRESULT delete 1.0 end # set outPercent [expr {$ActualInterest * 100.0}] # .fRresult.textRESULT insert end $outPercent set outPercentFmt [format "%11.6f" [expr {$ActualInterest * 100.0}]] .fRresult.textRESULT insert end $outPercentFmt } ## END OF PROC 'calc_interest' ##+######################################################################## ## 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_fixedwidth \ -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_fixedwidth \ -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 \"Lenders' Compound Interest Dream Crushed\" App ** 'e' = 2.71828... is the limit --- that is, compounding using infinitesimal time intervals turns 100% annual interest to a maximum of no more than 271.9% 'actual annual percentage rate' for the year. This Tk script is meant to demonstrate that there is a limit to the amount of interest that can be accumulated in a year --- no matter whether the interest is compounded annually, monthly, weekly, daily, hourly, every minute, or every second. In particular, this 'app' demonstrates that the sequence (1 + 1/n)^n approaches 2.71828... (dubbed the 'natural' number 'e'). The sequence (1 + 1/n)^n corresponds to a lender who wants 100% interest per year with options for various compounding rates. Say he/she wants to know how much more he/she could get from a borrower by compounding the interest annually, monthly, weekly, daily, hourly, every minute, or every second. For that lender, this 'app' can calculate the sequence of results (1 + 1)^1 (yearly), (1 + 1/12)^12 (monthly), (1 + 1/52)^52 (weekly), (1 + 1/365)^365 (daily), (1 + 1/8760)^8760 where 8760 = 365*24 (hourly), (1 + 1/525600)^525600 where 525600 = 8760*60 (by minute), and (1 + 1/31536000)^31536000 where 31546000 = 525600*60 (by second). ******************************* METHOD USED to perform the plot: (plot frames 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 32 million --- with integer-exponent values plotted along the x-axis. The y-axis goes from 0.0 to 300.0 (percent). 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 - top left (-1 million,+400.0) - bottom right (+32 million,-100.0) 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 for plotting on the Tk canvas. The points that may be plotted are: Approx. n (1 + 1/n)^n % increase -------- -------------- ---------- 1 100.0 12 261.303529 61 52 269.259695 8 365 271.456748 2 8760 271.812669 0.4 525600 271.827924 0.01 31536000 271.828178 0.001 So most of the possible increase (61%) is achieved by going from annual to monthly compounding. Weekly compounding will add another 8 percent. This is where the compounding dreams of unlimited riches for lenders is crushed. Back in the Middle Ages, lenders wondered how much interest they could collect if they compounded over shorter periods --- such as weeks or days. Unfortunately, they get only 8% more interest per year with weekly compounding --- and only 2% more with daily. A major increase (61%) comes with going from yearly to monthly compounding of interest. ************************** WINDOW (and canvas) RESIZE: ************************** If/when plotting is 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. *************************** ANOTHER PLANNED ENHANCEMENT (besides plotting): *************************** The 'base' annual interest rate (100.0) is shown in a 'disabled' 'entry' widget. Eventually, this utility may be changed to allow changing that entry --- to rates like 50, 25, 20 percent --- and less usurious rates such as 10, 7, and 3. For now, we keep things (the data coming at the user) simple, because 100 percent leads to the simple formula for the compounding sequence: (1 + 1/n)^n. For 50 percent base rate, the formula becomes (1 + 1/2n)^n For 25 percent base rate, the formula becomes (1 + 1/4n)^n For 20 percent base rate, the formula becomes (1 + 1/5n)^n For 10 percent base rate, the formula becomes (1 + 1/10n)^n For 5 percent base rate, the formula becomes (1 + 1/20n)^n The plan is to eventually allow for computing and plotting those sequences of numbers, for various n --- such as 1, 12, 52, 365, ... ******************************** 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: ##+################################################################ ##+################################################################ ## COMMENT OUT 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 GUI widgets --- such as entry widgets ## and the radiobutton widgets variable for ## the compounding-period. ##+################################################### set VARbaseInterest "100.0" set VARcompoundingPeriod "monthly" ##+################################################ ## Calculate the result for these initial values. ##+################################################ calc_interest advise_user \ "** Click a Compounding-Period radiobutton to calculate\ a new actual yearly interest percent. **" ##+################################################# ## 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