#!/usr/bin/wish ## ##+######################################################################## ## Linux/Unix NOTE: ## If the 'wish' interpreter is in a different directory, such as ## /usr/local/bin, you can make a soft-link from 'wish' there to ## /usr/bin/wish, as root, with a command like ## ln -s /usr/local/bin/wish /usr/bin/wish ##+######################################################################## ## Tk SCRIPT NAME: plotquik_LinesPoints_XYdataFromFile2or3cols.tk ## ## --- adapted from the script 'plot_quik_xy_file_2or3cols.tk' ## in the 'feHandyTools' subsystem of the 'Freedom Environment' ## subsystems at www.freedomenv.com --- adapted for putting a ## 'standalone' utility (with no 'source' statements) in the ## FE 'tkGooies' system. ## ##+####################################################################### ## PURPOSE: An intuitive, easy-to-use XY-AXIS FILE-PLOT UTILITY -- that ## reads data from a file, selecting 2 or 3 columns of data ## from among columns of data in the file --- ## to make 'presentation quality' plots QUICKLY. ## ## Oriented toward plots of many (tens or hundreds of) data points ## --- even thousands of data points --- via connecting lines --- ## or, optionally, using data point markers. ## ## (We can use a couple of checkbutton widgets to allow for ## choosing 'Lines' and/or 'Points'. ## We can allow both to be OFF --- to draw only titles, axes, ## tic-marks, and tic-mark labels --- no data lines or points.) ## _____________________________________________________________ ## ## This Tk script presents a GUI with a canvas widget showing a 2-D ## line (and/or point) plot --- with titles and tic-mark labels that ## can be dragged with the mouse. ## ## This script presents a GUI to prompt for a file name and ## to specify which column of data to use for the x-axis and ## which column of data to use for y1 data --- with an option ## to specify a 3rd column for y2 data. ## ## This utility assumes that we can use the same y-axis (limits ## and tic-marks) for both the y1 and y2 data. In other words, ## this utility is oriented toward comparing like-data over ## a common domain of x-values. For example, y1 and y2 could be ## economic data plotted over 12 months of two different years. ## _____________________________________________________________ ## ## This is a plotting-utility implementation using BASIC ## Tcl-Tk commands, i.e. not requiring an 'extension' of Tcl or Tk. ## ## Unfortunately, there do not seem to be any FAIRLY GENERAL, yet ## RELATIVELY SIMPLE, data-file plotting scripts at Tcl-Tk archive ## sites --- even in 2013, more than 20 years after the development ## of the necessary Tk canvas facilities to support plotting. ## ## Nor are such general, easy-to-use data-file plotting Tk scripts ## available via web searches on keyword strings such as ## 'bin wish canvas' or 'bin wish create line'. ## ## Even searches on 'canvas' and 'create line' on the wiki.tcl.tk site, ## in early 2013, yield only simplistic line plot 'demos' that are ## not suited to general and FAST line-and/or-point plots --- from ## text files of columnar data. ## ## This script is meant to fill that long-time void. ## _____________________________________________________________ ## ## SOURCES and CREDITS: ## ## The technique of dragging canvas items came from the Tcl-Tk demo in ## /usr/local/lib/tk4.0/demos/plot.tcl (on SGI-IRIX Unix, 1995). ## However, that script allowed the user to drag the data points. ## This script allows the user to drag titles and labels. ## ## On Linux (for example, Ubuntu 9.10, circa 2009), see ## /usr/share/doc/tk8.4/examples/plot.tcl ## or /usr/share/doc/tk8.5/examples/plot.tcl ## There are 60-plus other Tcl-Tk code examples in the ## 'examples' directory. ## ## See also 'create oval' plot scripts like ## 'items.tcl' and 'twind.tcl' in /usr/share/doc/tk8.x/examples/. ## These scripts provide examples of procs to move 'items' around ## on the canvas with a mouse. ## ##+##################################################################### ## ## INPUTS (via entry fields on the GUI): ## filename, x-y1-y2 column numbers, plot title, xy-axis-titles, ## xy-axis min-max, xy-axis tic-distance. ## ## OUTPUT: Intended for screen/window capture to an image file with a ## screen-capture tool (such as 'gnome-screenshot' on Linux). ## ## The image could be cropped with an image editor (such as ## 'mtpaint' on Linux) and the cropped image could be printed ## using an image view-print utility (such as 'eog' = Eye of ## Gnome, on Linux) --- or a web browser's print facility. ## ## If you are going to print the image, you will probably ## want to change the background color to bright white, via a ## 'CanvasColor' button at the top of the GUI --- to save ink. ## ## (Optionally, a Postscript-Print button and proc ## could be implemented.) ## ##+################# ## THE GUI WIDGETS: ## ## The options available to the user are indicated by ## the following 'sketch' of the GUI. ## ## In the sketch of the GUI below: ## ## 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-X indicates a Tk 'checkbutton' widget. ## CAPITAL-O indicates a Tk 'radiobutton' widget (if any). ## <----O----> indicates a Tk 'scale' widget (if any). ## ## ------------------------------------------------------------------------------------------------------- ## ## FRAMEnames ## VVVVVVVVVV ## ----------------------------------------------------------------------------------- ## tkPlotQuik - lines or points from 2 or 3 columns of data in a text file ## [window title] ## ----------------------------------------------------------------------------------- ## ## .fRbuttons {Exit}{Help}{CanvasColor}{UpdatePlot}{GetImage}{DwnCan}{UpCan}{PrtPreview}{Print} [entry field here ## shows a print command] ## .fRopts X DataLines X DataPoints X PlotBorder X SameAxisUnits for Y1 & Y2 ## [a grayed-out checkbutton, to emphasize same axis is used for y1 & y2] ## .fRfile {(Re)ReadFile} Data filename: ____________________________________________________{Browse4file} ## ## .fRtitle_main Plot title: _________________________________________________________________________________ ## ## .fRtitles_xy XaxisTitle:____________________________________________ YaxisTitle:___________________________________________ ## ## .fRlims_x Xcolumn: ___ XaxisMin: ______ XaxisMax: ______ XticDist:______ ## ## .fRlims_y1 Y1column: ___ YaxisMin: ______ YaxisMax: ______ YticDist:______ ## ## .fRlims_y2 Y2column: ___ ## ## .fRlow .fRlow.fRmsg .fRlow.fRplot ## [ an area here contains a guide [ an area here ## in a text or label widget contains a canvas widget ## ... ... ## many to be populated with ## ... ... ## lines 'items' whenever the 'UpdatePlot' button ## ... ... ## deep. in frame 'fRbuttons' is poked. ## ] ] ## ## --------------------------------------------------------------------------------------------------------------- ## ##+############## ## GUI components: ## ## From the GUI 'sketch' above, it is seen that the GUI consists of about ## ## - 11 button widgets ## - 16 label widgets ## - 16 entry widgets ## - 1 text widget (with scroll bars someday?) (or a label widget instead) ## - 1 canvas widget (with scroll bars someday?) ## - 3 checkbutton widgets ## - 0 radiobutton widgets ## - 0 listbox widgets ## - 0 scale widgets ## ##+##################################################################### ## STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name, win-position, win-color-scheme, ## fonts, widget-geom-parms, text-array-for-labels-etc, win-size-control). ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack ALL frames and sub-frames. ## 2) Define & pack ALL widgets within frames, frame by frame --- ## typically going top-to-bottom and/or left-to-right, frame by frame. ## ## After ALL widgets are defined for a frame, ## pack the widgets in the frame. ## ## 3) Define key and mouse/touchpad/touch-sensitive-screen action ## BINDINGS, if any. ## 4) Define PROCS, if any. ## 5) Additional GUI INITIALIZATION (typically with one or two ## of the procs), if needed. ## ## In more detail: ## ## 1a) Define ALL frames: ## ## - 'fRbuttons' to contain buttons --- Exit, Help, UpdatePlot, etc. ## - 'fRopts' to contain 3 checkbutton widgets (at least) ## - 'fRfile' to contain a label-and-entry widget pair and 2 button widgets. ## - 'fRtitle_main' to contain 1 label & 1 entry widget. ## - 'fRtitles_xy' to contain 2 pairs of label & entry widgets. ## - 'fRlims_x' to contain 4 pairs of label & entry widgets. ## - 'fRlims_y1' to contain 4 pairs of label & entry widgets. ## - 'fRlims_y2' to contain 3 pairs of label & entry widget. ## - 'fRlow' to contain the following 2 sub-frames. ## (These 7 frames are to be packed from top to bottom.) ## ## The following 2 sub-frames are to be packed side-by-side. ## ## - 'fRlow.fRmsg' to contain brief guide info in a label (or text) widget. ## ## - 'fRlow.fRplot' to contain a canvas widget (to be populated with ## 'items' whenever the 'UpdatePlot' button in frame ## 'fRbuttons' is poked --- actually whenever the 'update_plot' ## proc is executed). ## ## 1b) PACK FRAMES with appropriate '-side' '-anchor' '-fill' '-expand' ## parameters to get proper behavior of widgets within the frames, ## during window expansion. (Window expansion is allowed.) ## ## 2) DEFINE & PACK ALL WIDGETS in the frames -- basically going through ## frames & their interiors in top-to-bottom, left-to-right order: ## ## 3) Define BINDINGS: (See BINDINGS code section for all current bindings.) ## ## To Drag main-title, axis-titles, and tic-mark labels --- ## basically: ## - .fRame.can bind TAGmoveable "itemSelect .fRame.can %x %y" ## - bind .fRame.can "itemMove .fRame.can %x %y" ## - .fRame.can bind TAGmoveable ".fRame.can dtag TAGselected" ## ## To Change color of titles and tic-mark labels during move --- ## basically: ## .fRame.can bind TAGmoveable ".fRame.can itemconfig current -fill " ## .fRame.can bind TAGmoveable ".fRame.can itemconfig current -fill " ## ## 4) Define PROCS: ## (See PROCS code section for all current procs. Here are the main procs.) ## ## - 'find_file' Called by 'Browse4file' button. ## - 'read_data' Called by '(Re)ReadFile' button. ## ## - 'update_plot' Called by 'UpdatePlot' button and by 'downsize_can' and ## 'upsize_can' procs --- and at the bottom of this script ## in the 'Additional GUI Initialization' section. ## ## - 'set_margins' Called by 'update_plot' proc. ## ## - 'itemSelect' Called by a button1-press binding on the canvas. ## - 'itemMove' Called by a button1-motion binding on the canvas. ## ## - 'getSet_canvasColor' Called by 'CanvasColor' button. ## ## - 'downsize_canvas' Called by 'DwnCan' button. ## - 'upsize_canvas' Called by 'UpCan' button. ## - 'resize_win' Called by the 'downsize_canvas' and ## 'upsize_canvas' procs. ## ## - 'draw_border' Called by the 'update_plot' proc --- if ## the 'PlotBorder' checkbutton is ON. ## ## - 'getPut_image' - Called by 'GetImage' button. ## ## - 'print_preview' - Called by the 'PrtPreview' button. ## - 'print_plot' - Called by the 'Print' button. ## ## - 'popup_msgVarWithScroll' Called by 'Help' button. ## ## 5) Additional GUI initialization: ## Set various command and directory names. ## We can provide the GUI with a sample filename in the filename ## entry field --- and initial values in the other entry fields ## --- and plot 2 columns of data from the file when the GUI starts up. ## ##+####################################################################### ## 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 2013jul19 Started development, on Ubuntu 9.10, ## based on my code ## 'plot_quik_xy_file_20r3cols.tk' ## in the 'feHandyTools' subsystem of ## the 'Freedom Environment' subsystems ## at www.freedomenv.com --- adapted for ## donation of this code to the Tcl-Tk ## wiki at wiki.tcl.tk. Suspended. ## Updated by: Blaise Montandon 2017sep20 Restarted development for the FE ## 'tkGooies' system. ## Updated by: Blaise Montandon 2017oct08 Released this script at freedomenv.com. ##+######################################################################## ##+####################################################################### ## SET WINDOW TITLES. ##+####################################################################### wm title . "tkPlotQuik - lines or points from 2 or 3 columns of data in a text file" wm iconname . "Plot2or3Cols" # catch { wm title . "$env(FE_WIN_TITLE)" } # catch { wm iconname . "$env(FE_ICON_TITLE)" } ##+################################### ## SET THE TOP WINDOW POSITION. ##+################################### wm geometry . +15+30 # catch {eval wm geometry . "$env(FE_WIN_LOC_GEOM)" } ##+####################################################################### ## SET COLOR SCHEME (palette) for the window and its widgets. ##+####################################################################### if {1} { ## Grayish palette set Rpal255 210 set Gpal255 210 set Bpal255 210 } if {0} { ## Bluish palette set Rpal255 180 set Gpal255 180 set Bpal255 255 } if {0} { ## Greenish palette set Rpal255 180 set Gpal255 255 set Bpal255 180 } if {0} { ## Reddish palette set Rpal255 255 set Gpal255 180 set Bpal255 180 } ## If env vars R255,G255,B255 were set, then we could use the following ## format statement to get a hex value for specifying tkGUI colors, ## after 'catch'-ing the three values into Rpal255,Gpal255,Bpal255 Tcl vars. # catch { set Rpal255 "$env(R255)" } # catch { set Gpal255 "$env(G255)" } # catch { set Bpal255 "$env(B255)" } set hexCOLORpal [format "%02X%02X%02X" $Rpal255 $Gpal255 $Bpal255] ## Or could set palette from a hex-valued env var. # catch { set hexCOLORpal "$env(FE_PLOT_HEXWINCOLOR)" } tk_setPalette "#$hexCOLORpal" ##+####################################################### ## Set background colors for GUI widgets. ## ## Some other colors, such as colors for objects on the ## canvas --- text (titles and tic-mark labels) and lines ## (axes and tic-marks and data-lines and point-outlines) ## --- are set in the 'getSet_canvasColor' proc. ##+####################################################### set msgBKGD "#ffcccc" set entryBKGD "#f0f0f0" # set textBKGD "#f0f0f0" set textBKGD $msgBKGD set chkbuttBKGD "#c0c0c0" set chkbuttCOLORselect "#cccccc" ## Following widgets are not used, yet. # set listboxBKGD "#f0f0f0" # set radbuttBKGD "#c0c0c0" # set radbuttSELECTCOLOR "#cccccc" # set scaleBKGD "#f0f0f0" ##+####################################################################### ## SET FONT VARS to use in the 'font create' statements below. ## ## We use a VARIABLE-WIDTH FONT for label and button widgets. ## ## We use a FIXED-WIDTH FONT for entry fields ## and text widgets and listboxes, if any. ##------------------------------------------------------------------------ ## Some possible (similar) VARIABLE WIDTH fonts: ## Arial ## Bitstream Vera Sans ## Comic Sans MS ## DejaVu Sans ## Droid Sans ## FreeSans ## Liberation Sans ## Nimbus Sans L ## Trebuchet MS ## Verdana ## ## Some possible FIXED WIDTH fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## DejaVu Sans Mono ## Droid Sans Mono ## FreeMono ## Liberation Mono ## Nimbus Mono L ## TlwgMono ##+####################################################################### set FONTsize 14 set FONT_SMALLsize 12 set fontFamily "comic sans ms" set FONTparms_varwidth " -family {$fontFamily} \ -size -$FONTsize -weight bold -slant roman" set FONTparms_SMALL_varwidth " -family {$fontFamily} \ -size -$FONT_SMALLsize -weight normal -slant roman" # set fontFamily "dejavu sans mono" # set fontFamily "freemono" set fontFamily "liberation mono" set FONTparms_fixedwidth " -family {$fontFamily} \ -size -$FONTsize -weight bold -slant roman " set FONTparms_SMALL_fixedwidth " -family {$fontFamily} \ -size -$FONT_SMALLsize -weight bold -slant roman " ##+##################################################################### ## DEFINE (temporary) FONT VARS to be used in '-font' widget specs below ## --- and for some plot titles. ##+##################################################################### eval font create fontTEMP_varwidth $FONTparms_varwidth eval font create fontTEMP_fixedwidth $FONTparms_fixedwidth eval font create fontTEMP_button $FONTparms_varwidth eval font create fontTEMP_chkbutt $FONTparms_varwidth eval font create fontTEMP_label $FONTparms_varwidth eval font create fontTEMP_entry $FONTparms_fixedwidth eval font create fontTEMP_msg $FONTparms_fixedwidth eval font create fontTEMP_text $FONTparms_fixedwidth # eval font create fontTEMP_listbox $FONTparms_fixedwidth eval font create fontTEMP_SMALL_varwidth $FONTparms_SMALL_varwidth eval font create fontTEMP_SMALL_fixedwidth $FONTparms_SMALL_fixedwidth eval font create fontTEMP_SMALL_button $FONTparms_SMALL_varwidth eval font create fontTEMP_SMALL_chkbutt $FONTparms_SMALL_varwidth eval font create fontTEMP_SMALL_label $FONTparms_SMALL_varwidth eval font create fontTEMP_SMALL_entry $FONTparms_SMALL_fixedwidth eval font create fontTEMP_SMALL_msg $FONTparms_SMALL_fixedwidth eval font create fontTEMP_SMALL_text $FONTparms_SMALL_fixedwidth # eval font create fontTEMP_SMALL_listbox $FONTparms_SMALL_fixedwidth ## For the text in the plot: eval font create fontTEMP_plottext $FONTparms_varwidth ## For the text in the plot canvas: (NOT USED, yet) # eval font create fontTEMP_plottitle $FONTparms_varwidth # eval font create fontTEMP_axistitle $FONTparms_varwidth # eval font create fontTEMP_ticlabel $FONTparms_SMALL_varwidth ##+####################################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. padx, pady for buttons) ## ## Possible values for '-relief' are: ## flat, groove, raised, ridge, solid, and sunken. ##+####################################################################### ## For BUTTON widgets: set PADYpx_button 0 set PADXpx_button 0 set BDwidthPx_button 2 set RELIEF_button raised ## For LABEL widgets: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 set RELIEF_label flat ## For ENTRY widgets: set BDwidthPx_entry 2 set RELIEF_entry sunken ## For CHKBUTTON widgets: set PADYpx_chkbutton 0 set PADXpx_chkbutton 0 set BDwidthPx_chkbutton 2 # set RELIEF_chkbutton raised set RELIEF_chkbutton ridge ## For TEXT widgets: set BDwidthPx_text 2 # set RELIEF_text raised set RELIEF_text ridge ## For CANVAS widgets: # set BDwidthPx_canvas 2 # set RELIEF_canvas raised set BDwidthPx_canvas 0 set RELIEF_canvas flat set initCanWidthPx 200 set initCanHeightPx 200 set minCanHeightPx 24 ## For LISTBOX widgets: # set BDwidthPx_listbox 2 ##+############################################################## ## Set a TEXT-ARRAY to hold text for buttons, labels, and other ## widgets on the GUI. ## NOTE: This can aid INTERNATIONALIZATION. This array can ## be set according to a nation/region parameter. ##+############################################################## ## if { "$VARlocale" == "en"} ## For '.fRbuttons' frame: set aRtext(buttonEXIT) "Exit" set aRtext(buttonHELP) "Help" set aRtext(buttonCANCOLOR) "CanvasColor" set aRtext(buttonUPDATE) "UpdatePlot" set aRtext(buttonDWNCAN) "DwnCan" set aRtext(buttonUPCAN) "UpCan" set aRtext(buttonGETIMG) "GetImage" set aRtext(buttonPRTPREVIEW) "PrtPreview" set aRtext(buttonPRINT) "Print" set aRtext(labelPRTCMD) "Print command:" ## For '.fRopts' frame: set aRtext(chkbuttLINES) "DataLines" set aRtext(chkbuttPOINTS) "DataPoints" set aRtext(chkbuttBORDER) "PlotBorder" set aRtext(chkbuttY1Y2AXIS) "SameAxisUnits for Y1 & Y2" ## For '.fRfile' frame: set aRtext(buttonREADFILE) "(Re)ReadFile" set aRtext(labelFILENAME) "Data filename:" set aRtext(buttonBROWSE4FILE) "Browse4file..." ## For '.fRtitle_main' frame: set aRtext(labelMAINTITLE) "Plot Title:" ## For '.fRtitles_xy' frame: set aRtext(labelXAXISTITLE) "XaxisTitle:" set aRtext(labelYAXISTITLE) "YaxisTitle:" ## For '.fRlims_x' frame: set aRtext(labelXCOLNUM) "Xcolnum:" set aRtext(labelXaxisMIN) "XaxisMin:" set aRtext(labelXaxisMAX) "XaxisMax:" set aRtext(labelXaxisTICDIST) "XticDist:" ## For '.fRlims_y1' frame: set aRtext(labelY1COLNUM) "Y1colnum:" set aRtext(labelY1axisMIN) "YaxisMin:" set aRtext(labelY1axisMAX) "YaxisMax:" set aRtext(labelY1axisTICDIST) "YticDist:" ## For '.fRlims_y2' frame: set aRtext(labelY2COLNUM) "Y2colnum:" # set aRtext(labelY2axisMIN) "Y2axisMin:" # set aRtext(labelY2axisMAX) "Y2axisMax:" # set aRtext(labelY2axisTICDIST) "YticDist:" ## For '.fRlow.fRmsg' frame: set aRtext(labelHELPMSG) \ "To the right is a 'canvas' to contain a line-or-points plot of data read from 2 or 3 columns in a file. Recommended order of actions: 1. Increase the canvas size (& plot axes length) with the 'UpCan' button. Down- size with 'DwnCan'. Use the 'CanvasColor' button to change canvas background color. 2. Get or enter filename. Set ColNums wanted, then click 'ReadFile'. Change Min-Max values and titles if needed. 3. After setting titles and other entry fields, esp. Min-Max values, use the 'UpdatePlot' button. 4. You can drag any of the titles or labels with mouse button 1. When you use the 'UpdatePlot' button, labels and titles are returned to their initial locations (handy to restore labels if pulled off-canvas). You can drag labels back where wanted. 5. You can use screen-grab, image- editor, and image-view-print utilities to make an image file or to print the plot." ## END OF if { "$VARlocale" == "en"} ##+################################################################### ## Set a MINSIZE of the window (roughly). ## ## For WIDTH, allow for the minwidth of the '.fRbuttons' frame: ## about 9 widgets --- Exit, Help, UpdatePlot, etc. buttons. ## ## For HEIGHT, allow ## about 1 char high for the '.fRbuttons' frame and entry frames ## below that frame, ## about 24 pixels high for the '.fRlow.fRplot' frame. ##+################################################################### set minWinWidthPx [font measure fontTEMP_varwidth \ "$aRtext(buttonEXIT) $aRtext(buttonHELP) \ $aRtext(buttonCANCOLOR) $aRtext(buttonUPDATE) \ $aRtext(buttonDWNCAN) $aRtext(buttonUPCAN) \ $aRtext(buttonGETIMG) \ $aRtext(buttonPRTPREVIEW) $aRtext(buttonPRINT)"] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 9 x 4 pixels/widget for borders/padding for ## at least 9 button widgets. set minWinWidthPx [expr {44 + $minWinWidthPx}] ## MIN HEIGHT --- ## for the 9 stacked frames allow ## 1 char high for 'fRbuttons' ## 1 char high for 'fRopts' ## 1 char high for 'fRfile' ## 1 char high for 'fRtitle_main' ## 1 char high for 'fRtitles_xy' ## 1 char high for 'fRlims_x' ## 1 char high for 'fRlims_y1' ## 1 char high for 'fRlims_y2' ## 24 pixels high for 'fRplot'. set CharHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {8 * $CharHeightPx}] set minWinHeightPx [expr {$minWinHeightPx + 24}] ## Add about 28 pixels for top-bottom window decoration, ## about 8x4 pixels for each of the 8 stacked frames and their ## widgets (their borders/padding). set minWinHeightPx [expr {$minWinHeightPx + 60}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## We allow the window to be resizable and we pack the canvas with ## '-fill both -expand 1' so that the canvas can be enlarged by enlarging ## the window. ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ##+#################################################################### ##+#################################################################### ## DEFINE *ALL* THE FRAMES -- (top to bottom): ## ## - 'fRbuttons' ## - 'fRopts' ## - 'fRfile' ## - 'fRtitle_main' ## - 'fRtitles_xy' ## - 'fRlims_x' ## - 'fRlims_y1' ## - 'fRlims_y2' ## - 'fRlow' ## ## Sub-frames: ## - 'fRlow.fRmsg' ## - 'fRlow.fRplot' ## ##+#################################################################### ## FOR TESTING change 0 to 1: ## (Example1: To see appearance of frames when their 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 .fRopts -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRfile -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRtitle_main -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRtitles_xy -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRlims_x -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRlims_y1 -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRlims_y2 -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRlow -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRlow.fRmsg -relief raised -bd 2 frame .fRlow.fRplot -relief raised -bd 2 ##+######################################################## ## PACK *ALL* the FRAMES. ##+######################################################## ## PACK THE FRAMES IN SEPARATELY, in order to ## experiment with different behaviors in window expansion. ## ## (We may want to change the top-down order of these frames.) ##+######################################################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRopts \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRfile \ -side top \ -anchor nw \ -fill x \ -expand 1 pack .fRlims_x \ -side top \ -anchor nw \ -fill x \ -expand 1 pack .fRlims_y1 \ -side top \ -anchor nw \ -fill x \ -expand 1 pack .fRlims_y2 \ -side top \ -anchor nw \ -fill x \ -expand 1 pack .fRtitle_main \ -side top \ -anchor nw \ -fill x \ -expand 1 pack .fRtitles_xy \ -side top \ -anchor nw \ -fill x \ -expand 1 pack .fRlow \ -side top \ -anchor center \ -fill both \ -expand 1 pack .fRlow.fRmsg \ -side left \ -anchor nw \ -fill y \ -expand 0 pack .fRlow.fRplot \ -side right \ -anchor ne \ -fill both \ -expand 1 ##+################################################################ ## Finished defining all the frames. ##+################################################################ ## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES. ##+################################################################ ##+################################################################ ##+######################################################## ## IN THE 'fRbuttons' frame -- ## DEFINE ~9 BUTTON widgets and several CHKBUTTON widgets. ## THEN PACK THEM. ##+######################################################## button .fRbuttons.buttExit \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {exit} button .fRbuttons.buttHelp \ -text "$aRtext(buttonHELP)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {popup_msgVarWithScroll .topHelp "$HELPtext" +10+30} button .fRbuttons.buttCanvasColor \ -text "$aRtext(buttonCANCOLOR)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {getSet_canvasColor} button .fRbuttons.buttUpdate \ -text "$aRtext(buttonUPDATE)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {update_plot} button .fRbuttons.buttDWNwin \ -text "$aRtext(buttonDWNCAN)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {downsize_canvas} button .fRbuttons.buttUPwin \ -text "$aRtext(buttonUPCAN)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {upsize_canvas} button .fRbuttons.buttGetImg \ -text "$aRtext(buttonGETIMG)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {getPut_image} button .fRbuttons.buttPrtPreview \ -text "$aRtext(buttonPRTPREVIEW)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {print_preview} button .fRbuttons.buttPrint \ -text "$aRtext(buttonPRINT)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {print_plot} label .fRbuttons.labPRTCMD \ -text "$aRtext(labelPRTCMD)" \ -font fontTEMP_label \ -anchor w \ -justify left \ -padx $PADXpx_label \ -pady $PADYpx_label \ -relief $RELIEF_label ## We initialize this var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYprintcmd "$PRINTcmd" entry .fRbuttons.entPRTCMD \ -textvariable ENTRYprintcmd \ -width 25 \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry ## Pack the widgets in frame '.fRbuttons'. ## (We might want to change the order someday.) pack .fRbuttons.buttExit \ .fRbuttons.buttHelp \ .fRbuttons.buttCanvasColor \ .fRbuttons.buttUpdate \ .fRbuttons.buttGetImg \ .fRbuttons.buttDWNwin \ .fRbuttons.buttUPwin \ .fRbuttons.buttPrint \ .fRbuttons.buttPrtPreview \ .fRbuttons.labPRTCMD \ -side left \ -anchor center \ -fill none \ -expand 0 pack .fRbuttons.entPRTCMD \ -side left \ -anchor w \ -fill x \ -expand 1 ##+######################################################## ## IN THE 'fRopts' frame -- ## DEFINE several CHECKBUTTON widgets. THEN PACK THEM. ##+######################################################## set dataLines0or1 1 checkbutton .fRopts.chkbuttLINES \ -variable dataLines0or1 \ -text "$aRtext(chkbuttLINES)" \ -font fontTEMP_chkbutt \ -selectcolor "$chkbuttCOLORselect" \ -padx $PADXpx_chkbutton \ -pady $PADYpx_chkbutton \ -bd $BDwidthPx_chkbutton \ -relief $RELIEF_chkbutton set dataPoints0or1 0 checkbutton .fRopts.chkbuttPOINTS \ -variable dataPoints0or1 \ -text "$aRtext(chkbuttPOINTS)" \ -font fontTEMP_chkbutt \ -selectcolor "$chkbuttCOLORselect" \ -padx $PADXpx_chkbutton \ -pady $PADYpx_chkbutton \ -bd $BDwidthPx_chkbutton \ -relief $RELIEF_chkbutton set plotBorder0or1 0 checkbutton .fRopts.chkbuttBORDER \ -variable plotBorder0or1 \ -text "$aRtext(chkbuttBORDER)" \ -font fontTEMP_chkbutt \ -selectcolor "$chkbuttCOLORselect" \ -padx $PADXpx_chkbutton \ -pady $PADYpx_chkbutton \ -bd $BDwidthPx_chkbutton \ -relief $RELIEF_chkbutton set sameY1Y2axis0or1 1 checkbutton .fRopts.chkbuttY1Y2AXIS \ -variable sameY1Y2axis0or1 \ -text "$aRtext(chkbuttY1Y2AXIS)" \ -font fontTEMP_chkbutt \ -selectcolor "$chkbuttCOLORselect" \ -padx $PADXpx_chkbutton \ -pady $PADYpx_chkbutton \ -bd $BDwidthPx_chkbutton \ -relief $RELIEF_chkbutton \ -state disabled ## Pack the widgets in frame '.fRopts'. pack .fRopts.chkbuttLINES \ .fRopts.chkbuttPOINTS \ .fRopts.chkbuttBORDER \ .fRopts.chkbuttY1Y2AXIS \ -side left \ -anchor center \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRfile' frame -- ## DEFINE 1 ENTRY & 2 BUTTON widgets. THEN PACK THEM. ##+######################################################## button .fRfile.buttREADFILE \ -text "$aRtext(buttonREADFILE)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {read_data} label .fRfile.labFILENAME \ -text "$aRtext(labelFILENAME)" \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize the filename var at the bottom of this script, ## in the GUI inititalization section. Examples: # set ENTRYfilename "$feDIR_data/DATA2cols_lifeExpectancyInUS_yearVSyears.txt" # set ENTRYfilename "$feDIR_data/DATA2cols_worldpopulation_yearVSmillions.txt" entry .fRfile.entFILENAME \ -textvariable ENTRYfilename \ -width 40 \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry button .fRfile.buttBROWSE4FILE \ -text "$aRtext(buttonBROWSE4FILE)" \ -font fontTEMP_button \ -padx $PADXpx_button \ -pady $PADYpx_button \ -bd $BDwidthPx_button \ -relief $RELIEF_button \ -command {find_file} ## Pack the widgets in frame '.fRfile'. pack .fRfile.buttREADFILE \ .fRfile.labFILENAME \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRfile.entFILENAME \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRfile.buttBROWSE4FILE \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRtitle_main' frame -- ## DEFINE 1 LABEL & 1 ENTRY WIDGET. THEN PACK THEM. ##+######################################################## label .fRtitle_main.labMAINTITLE \ -text "$aRtext(labelMAINTITLE)" \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this entry var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYtitleMain " MAIN PLOT TITLE here." entry .fRtitle_main.entMAINTITLE \ -textvariable ENTRYtitleMain \ -width 90 \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry ##+######################################### ## Pack the widgets in frame '.fRtitle_main'. ##+######################################### pack .fRtitle_main.labMAINTITLE \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRtitle_main.entMAINTITLE \ -side left \ -anchor w \ -fill x \ -expand 1 ##+############################################################ ## IN THE 'fRtitles_xy' frame -- ## DEFINE 2 pairs of LABEL & ENTRY widgets for x-axis & y-axis. ## THEN PACK the 4 widgets. ##+############################################################ label .fRtitles_xy.labXaxisTitle \ -text "$aRtext(labelXAXISTITLE)" \ -font fontTEMP_label \ -anchor w \ -justify left \ -padx $PADXpx_label \ -pady $PADYpx_label \ -relief $RELIEF_label ## We initialize this var at the bottom of this script, ## in the GUI inititalization section. # set ENTRYtitleXaxis " X-AXIS TITLE here." entry .fRtitles_xy.entXaxisTitle \ -textvariable ENTRYtitleXaxis \ -width 40 \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry ##+######################################################## ## Now, for the y-axis: ##+######################################################## label .fRtitles_xy.labYaxisTitle \ -text "$aRtext(labelYAXISTITLE)" \ -font fontTEMP_label \ -anchor w \ -justify left \ -padx $PADXpx_label \ -pady $PADYpx_label \ -relief $RELIEF_label ## We initialize this var at the bottom of this script, ## in the GUI inititalization section. # set ENTRYtitleY1axis " Y-AXIS TITLE here. " entry .fRtitles_xy.entYaxisTitle \ -textvariable ENTRYtitleY1axis \ -width 40 \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry ## PACK x-and-y axis-title widgets in frame '.fRtitles_xy'. pack .fRtitles_xy.labXaxisTitle \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRtitles_xy.entXaxisTitle \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRtitles_xy.labYaxisTitle \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRtitles_xy.entYaxisTitle \ -side left \ -anchor w \ -fill x \ -expand 1 ##+######################################################## ## Set character-widths of label and entry widgets in the ## 'fRlims_x' 'fRlims_y1' and 'fRlims_y2' frames --- to be ## used below. This allows for easy and consistent changes. ##+######################################################## set LabelWidthChars_forColNums 8 set EntryWidthChars_forColNums 4 set LabelWidthChars_forMinMax 9 set EntryWidthChars_forMinMax 10 set LabelWidthChars_forTicDist 9 set EntryWidthChars_forTicDist 8 ##+######################################################## ## IN THE 'fRlims_x' frame -- ## DEFINE 4 pairs of LABEL & ENTRY widgets for the x-axis. ## THEN PACK THEM. ##+######################################################## label .fRlims_x.labXCOLNUM \ -text "$aRtext(labelXCOLNUM)" \ -width $LabelWidthChars_forColNums \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYxcolNUM 1 entry .fRlims_x.entXCOLNUM \ -textvariable ENTRYxcolNUM \ -width $EntryWidthChars_forColNums \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry label .fRlims_x.labXaxisMIN \ -text "$aRtext(labelXaxisMIN)" \ -width $LabelWidthChars_forMinMax \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYxaxisMIN "0" entry .fRlims_x.entXaxisMIN \ -textvariable ENTRYxaxisMIN \ -width $EntryWidthChars_forMinMax \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry label .fRlims_x.labXaxisMAX \ -text "$aRtext(labelXaxisMAX)" \ -width $LabelWidthChars_forMinMax \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYxaxisMAX "720" entry .fRlims_x.entXaxisMAX \ -textvariable ENTRYxaxisMAX \ -width $EntryWidthChars_forMinMax \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry label .fRlims_x.labXaxisTICDIST \ -text "$aRtext(labelXaxisTICDIST)" \ -width $LabelWidthChars_forTicDist \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYxaxisTICDIST "10.0" entry .fRlims_x.entXaxisTICDIST \ -textvariable ENTRYxaxisTICDIST \ -width $EntryWidthChars_forTicDist \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry ##+########################################## ## Pack the 4 pairs of label & entry widgets ## in frame 'fRlims_x'. ##+########################################## pack .fRlims_x.labXCOLNUM \ .fRlims_x.entXCOLNUM \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_x.labXaxisMIN \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_x.entXaxisMIN \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRlims_x.labXaxisMAX \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_x.entXaxisMAX \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRlims_x.labXaxisTICDIST \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_x.entXaxisTICDIST \ -side left \ -anchor w \ -fill x \ -expand 1 ##+######################################################## ## IN THE 'fRlims_y1' frame -- ## DEFINE 4 pairs of LABEL & ENTRY widgets for the y1-axis. ## THEN PACK THEM. ##+######################################################## label .fRlims_y1.labY1COLNUM \ -text "$aRtext(labelY1COLNUM)" \ -width $LabelWidthChars_forColNums \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYy1colNUM 1 entry .fRlims_y1.entY1COLNUM \ -textvariable ENTRYy1colNUM \ -width $EntryWidthChars_forColNums \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry label .fRlims_y1.labY1axisMIN \ -text "$aRtext(labelY1axisMIN)" \ -width $LabelWidthChars_forMinMax \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYy1axisMIN "-100" entry .fRlims_y1.entY1axisMIN \ -textvariable ENTRYy1axisMIN \ -width $EntryWidthChars_forMinMax \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry label .fRlims_y1.labY1axisMAX \ -text "$aRtext(labelY1axisMAX)" \ -width $LabelWidthChars_forMinMax \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYy1axisMAX "100" entry .fRlims_y1.entY1axisMAX \ -textvariable ENTRYy1axisMAX \ -width $EntryWidthChars_forMinMax \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry label .fRlims_y1.labY1axisTICDIST \ -text "$aRtext(labelY1axisTICDIST)" \ -width $LabelWidthChars_forTicDist \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYy1axisTICDIST 10.0 entry .fRlims_y1.entY1axisTICDIST \ -textvariable ENTRYy1axisTICDIST \ -width $EntryWidthChars_forTicDist \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry ##+########################################### ## Pack the 4 pairs of label and entry widgets ## in frame 'fRlims_y1'. ##+########################################### pack .fRlims_y1.labY1COLNUM \ .fRlims_y1.entY1COLNUM \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_y1.labY1axisMIN \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_y1.entY1axisMIN \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRlims_y1.labY1axisMAX \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_y1.entY1axisMAX \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRlims_y1.labY1axisTICDIST \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_y1.entY1axisTICDIST \ -side left \ -anchor w \ -fill x \ -expand 1 ##+######################################################## ## IN THE 'fRlims_y2' frame -- ## DEFINE 1 pair of LABEL & ENTRY widgets for the y2-axis. ## THEN PACK THEM. ##+######################################################## label .fRlims_y2.labY2COLNUM \ -text "$aRtext(labelY2COLNUM)" \ -width $LabelWidthChars_forColNums \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYy2colNUM 1 entry .fRlims_y2.entY2COLNUM \ -textvariable ENTRYy2colNUM \ -width $EntryWidthChars_forColNums \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry ## THE FOLLOWING Y2 AXIS MIN,MAX, and TICDIST ## 'entry' FIELDS ARE NOT NEEDED IN THIS IMPLEMENTATION. if {0} { label .fRlims_y2.labY2axisMIN \ -text "$aRtext(labelY2axisMIN)" \ -width $LabelWidthChars_forMinMax \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYy2axisMIN "-100" entry .fRlims_y2.entY2axisMIN \ -textvariable ENTRYy2axisMIN \ -width $EntryWidthChars_forMinMax \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry \ label .fRlims_y2.labY2axisMAX \ -text "$aRtext(labelY2axisMAX)" \ -width $LabelWidthChars_forMinMax \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYy2axisMAX "100" entry .fRlims_y2.entY2axisMAX \ -textvariable ENTRYy2axisMAX \ -width $EntryWidthChars_forMinMax \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry label .fRlims_y2.labY2axisTICIDST \ -text "$aRtext(labelY2axisTICDIST)" \ -width $LabelWidthChars_forTicDist \ -font fontTEMP_label \ -anchor e \ -justify right \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label ## We initialize this 'entry' var at the bottom of this script, ## in the GUI inititalization section. Example: # set ENTRYy2axisTICDIST 10.0 entry .fRlims_y2.entY2axisTICDIST \ -textvariable ENTRYy2axisTICDIST \ -width $EntryWidthChars_forTicDist \ -font fontTEMP_entry \ -bg $entryBKGD \ -bd $BDwidthPx_entry \ -relief $RELIEF_entry } ## END OF 'if {0}' section to comment this code. ##+########################################### ## Pack the 1 pair of label and entry widgets ## in frame 'fRlims_y2'. ##+########################################### pack .fRlims_y2.labY2COLNUM \ .fRlims_y2.entY2COLNUM \ -side left \ -anchor w \ -fill none \ -expand 0 ## COMMENT THE PACKING OF THE NOT-USED-YET WIDGETS. if {0} { pack .fRlims_y2.labY2axisMIN \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_y2.entY2axisMIN \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRlims_y2.labY2axisMAX \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_y2.entY2axisMAX \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRlims_y2.labY2axisTICDIST \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRlims_y2.entY2axisTICDIST \ -side left \ -anchor w \ -fill x \ -expand 1 } ## END OF the 'if {0}' section to comment this code. ##+######################################################## ## IN THE 'fRlow.fRmsg' frame -- DEFINE 1 LABEL WIDGET. ## THEN PACK IT. ##+######################################################## label .fRlow.fRmsg.lab \ -text "$aRtext(labelHELPMSG)" \ -font fontTEMP_SMALL_msg \ -anchor nw \ -justify left \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label \ -relief $RELIEF_label pack .fRlow.fRmsg.lab \ -side left \ -anchor nw \ -fill none \ -expand 0 ##+######################################################## ## IN THE 'fRlow.fRplot' frame -- DEFINE 1 CANVAS WIDGET. ## THEN PACK IT. ## ## We set 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' by Welch, Jones, Hobbs. ##+######################################################## ## Instead of hard-coding an initial canvas size, like this ## set canWidthPx 500 ## set canHeightPx 375 ## we set the canvas size in proportion to the screen size. set SCRNsizexPx [winfo screenwidth .] set SCRNsizeyPx [winfo screenheight .] set canWidthPx [ expr {int(6 * $SCRNsizexPx / 10)} ] set canHeightPx [ expr {int(6 * $SCRNsizeyPx / 10)} ] ## FOR TESTING: # puts "canWidth: $canWidthPx" # puts "canHeight: $canHeightPx" canvas .fRlow.fRplot.can \ -width $canWidthPx \ -height $canHeightPx \ -highlightthickness 0 \ -borderwidth 0 \ -relief $RELIEF_canvas pack .fRlow.fRplot.can \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+################################################################## ## Finished defining-and-packing widgets in all the frames. ##+################################################################## ## END OF MAIN GUI BUILDING SECTION. ##+################################################################## ##+################################################################## ##+################################################################### ##+################################################################### ## DEFINE BINDINGS -- for mouse actions: ## - to Drag titles and tic-mark labels (and images). ## - to Change color of text items during their move. ## ## NOTE1: ## In the following, instead of using 'TAGtitles' and 'TAGticmarks' and ## 'TAGimages', we use 'TAGmoveable' for any movable item, and we tag ## the moveable items with tag 'TAGmoveable' instead of (or in addition to) ## 'TAGtext' and 'TAGtitles' and 'TAGticmarks' and 'TAGimages'. ## ## But 'TAGtitles' and 'TAGticmarks' and 'TAGimages' may prove useful for ## other operations on titles or ticmarks or images. ## ## NOTE2: ## The 'itemSelect' proc puts the tag 'TAGselected' on the item selected. ## The tag 'TAGselected' is used in the 'itemMove' proc to determine the ## item to be moved. ## ## NOTE3: ## We use tag 'TAGtext' on titles and tic-mark labels, to support a color ## change while they are being moved. ##+################################################################### ##+################################################################### ##+############################################## ## To Drag titles and tic-mark labels and images: ##+################################################# ## The tag 'TAGmoveable' limits the select-and-move ## operations to items that we want to make moveable ## on the canvas. ## The item to be moved is tagged with the tag ## 'TAGselected', which should be the only item ## with that tag. ##+################################################# .fRlow.fRplot.can bind TAGmoveable \ "itemSelect .fRlow.fRplot.can %x %y" bind .fRlow.fRplot.can \ "itemMove .fRlow.fRplot.can %x %y" ## Remove the 'TAGselected' tag when button is released. .fRlow.fRplot.can bind TAGmoveable \ ".fRlow.fRplot.can dtag TAGselected" ##+############################################### ## To Change color of text items (titles and ## tic-mark labels) during their move: ##+############################################### .fRlow.fRplot.can bind TAGtext \ { .fRlow.fRplot.can itemconfig current -fill "#$textCOLORselHEX" } .fRlow.fRplot.can bind TAGtext \ { .fRlow.fRplot.can itemconfig current -fill "#$textCOLORregHEX" } ##+################################################################### ##+################################################################### ## DEFINE PROCEDURES: ## ## Main procs: ## ## - 'find_file' - Called by the 'Browse4File' button. ## To get a filename of a data file. ## Puts the filename in the filename entry field. ## ## - 'read_data' - Called by the '(Re)ReadFile' button. ## Loads data from the datafile into data arrays ## according to the x,y1,y2 colnums specified. ## ## - 'set_lims_ticdist' - Called by proc 'read_data', twice --- for ## x-axis and for y-axis. ## Sets 'suggested' values for the x and y axis ## min,max,ticdist entry fields --- based on ## max-and-min x-data-values and on max-and-min y-data-values. ## I.e. 2 numbers in, 3 numbers out. ## ## - 'set_nice_upper_bound' - Called by proc 'set_lims_ticdist'. ## Given a positive number returns a nice 'base10' number ## that is not smaller. ## ## - 'update_plot' - Called by the 'UpdatePlot' button --- and by the ## 'downsize_canvas' and 'upsize_canvas' procs --- ## and in the 'Additional GUI Initialization' section ## at the bottom of this script. ## ## Performs the plot including titles, x and y axes, ## tic-marks, tic-mark labels, and the data lines/points. ## ## - 'set_margins' - Called by the 'update_plot' proc. ## Sets margins around 'central plot rectangle'. ## ## - 'get_deciPlaces' - Called by the 'update_plot' proc. ## Gets decimal places from a TicDist entry. ## ## - 'format_deciPlaces' - Called by the 'update_plot' proc. ## Formats tic-mark-label text according to ## decimal places found by proc 'get_deciPlaces'. ## ## - 'itemSelect' - Called by a ButtonPress-1 binding on the canvas. ## Selects a canvas item to move. ## ## - 'itemMove' - Called by a Button1-Motion binding on the canvas. ## Moves a selected canvas item. ## ## - 'draw_border' - Called by the 'update_plot' proc --- ## if the 'PlotBorder' checkbutton is ON. ## Draws a border (a line) around the plot. ## ## - 'getSet_canvasColor' - Called by the 'CanvasColor' button. ## Gets-and-sets a background color for canvas, ## via a separate RGB color-selector Tk script. ## ## - 'update_cancolor_button' - Called by proc 'getSet_canvasColor' and in the ## Additional GUI Initialization' section at the ## bottom of this script. ## Sets the background color of the 'CanvasColor' ## button to the same color as the current ## background color of the canvas. ## ## - 'set_plotColors' - Called by proc 'getSet_canvasColor' --- and in the ## 'Additional GUI Initialization' section at the ## bottom of this script. ## From the current canvas color, sets appropriate ## contrasting colors --- like a color for text items ## (titles and tic-mark-labes) and lines (axes, tic-marks, ## and data-lines). ## ## - 'blackORwhite_farthestFromHexRGBcolor' - Called by proc 'set_plotColors'. ## Sets a contrasting color for text and lines. ## ## - 'invert_hexRGBcolor' - Called by proc 'set_plotColors'. ## (Alternative to 'blackORwhite_farthestFromHexRGBcolor'.) ## ## - 'augment_hexRGBcolor' - Called by proc 'set_plotColors'. ## Sets a color for text items when they are moved. ## ## Other, utility procs: ## ## - 'downsize_canvas' - Called by the 'DwnCan' button. ## Reduces the GUI (and canvas) size by a fixed percentage. ## ## - 'upsize_canvas' - Called by the 'UpCan' button. ## Increases the GUI (and canvas) size by a fixed percentage. ## ## - 'resize_win' - Called by the 'downsize_canvas' and 'upsize_canvas' procs. ## Increase/decrease GUI window size by a factor. ## ## - 'getPut_image' - Called by the 'GetImage' button. ## Gets and puts an image on the canvas. ## ## - 'print_preview' - Called by the 'PrtPreview' button. ## Puts plot image in Postscript file and shows it to user. ## ## - 'print_plot' - Called by the 'PrtPlot' button. ## Puts plot image in Postscript file and sends to a printer. ## ## - 'popup_shortmsg' - Called by various procs --- 'update_plot' and maybe more. ## ## - 'popup_msgVarWithScroll' - Called by the 'Help' button. ## Used to popup msgs, such as help, to user. ## Could be used in the 'popup_shortmsg' proc. ## ##+################################################################### ##+################################################################### ##+##################################################################### ## PROC 'find_file' ##+##################################################################### ## PURPOSE: To load a filename into the ENTRYfilename global var. ## ## METHOD: Uses the Tcl-Tk 'tk_getOpenFile' dialog utility to ## allow the user to navigate to a data file for plotting. ## ## CALLED BY: the 'Browse4File' button ##+##################################################################### proc find_file { } { global ENTRYfilename env DIRdatafiles if {"$ENTRYfilename" != ""} { set ENTRYfilename [string trim $ENTRYfilename] set DIRdatafiles [file dirname "$ENTRYfilename"] } ## '-parent .' signifies that this GUI is the parent window ## of the 'tk_getOpenFile' window. set FILEselected [tk_getOpenFile -parent . \ -title "Select a file of columnar-data." \ -initialdir "$DIRdatafiles" ] if { "$FILEselected" == "" } { ## Simply use 'return' to leave ENTRYfilename entry unchanged. return } else { set ENTRYfilename "$FILEselected" ################################################ ## Make the end of the filename visible in the ## filename entry field. ################################################ .fRfile.entFILENAME xview end ########################################################## ## CLEAR THE CANVAS WIDGET in preparation for using data ## from a new/different file. ########################################################## .fRlow.fRplot.can delete all return } } ## END OF PROC 'find_file' ##+##################################################################### ## PROC 'read_data' ##+##################################################################### ## PURPOSE: ## This procedure is invoked to read 2 or 3 columns of data ## from the currently specified file into Tcl array variables ## aRxvals, aRy1vals, and, optionally, aRy2vals. ## ## As the data lines are read, this proc puts the X, Y1, Y2 min,max values ## into variables XdataMIN, XdataMAX, Y1dataMIN, Y1dataMAX, and, optionally, ## Y2dataMIN, Y2dataMAX. ## ## These min-max values are supplied to proc 'set_lims_ticdist' ## to provide 'suggested' values for the entry fields ## ENTRYxaxisMIN, ENTRYxaxisMAX, ENTRYxaxisTICDIST ## and ## ENTRYy1axisMIN, ENTRYy1axisMAX, ENTRYy1axisTICDIST ## ## Arguments: none (global variables such as column numbers in entry field ## variables) ## ## CALLED BY: the '(Re)ReadFile' button ##+##################################################################### proc read_data {} { ## Inputs global ENTRYfilename ENTRYxcolNUM ENTRYy1colNUM ENTRYy2colNUM ## Make sure the colNUM vars have no leading or trailing spaces. ## In particular, make sure "$ENTRYy2colNUM" == "" (null) if it is left blank. set ENTRYxcolNUM [string trim $ENTRYxcolNUM] set ENTRYy1colNUM [string trim $ENTRYy1colNUM] set ENTRYy2colNUM [string trim $ENTRYy2colNUM] ## Besides read numeric data, we read titles from the first 3 lines of the file. global ENTRYtitleMain ENTRYtitleXaxis ENTRYtitleY1axis ## Outputs - the data, in Tcl arrays global aRxvals aRy1vals aRy2vals NXvals ## Outputs for possible use outside this proc. global XdataMAX XdataMIN Y1dataMAX Y1dataMIN Y2dataMAX Y2dataMIN ## Output - 'suggested' values in the min,max,ticdist entry fields. global ENTRYxaxisMIN ENTRYxaxisMAX ENTRYxaxisTICDIST \ ENTRYy1axisMIN ENTRYy1axisMAX ENTRYy1axisTICDIST if { "$ENTRYfilename" == "" } { set ERRmessage "Filename was not specified." set ERRtitle "Input Err" popup_shortmsg "$ERRtitle" "$ERRmessage" return } if {![file exists "$ENTRYfilename"]} { set ERRmessage \ "FILENAME $ENTRYfilename NOT FOUND." set ERRtitle "Input Err" popup_shortmsg "$ERRtitle" "$ERRmessage" return } ####################################### ## Prepare to use new array variables. ####################################### catch {unset aRxvals aRy1vals aRy2vals} set NXvals 0 set Nrecs 0 ####################################### ## Open the file. ####################################### set fid [open "$ENTRYfilename"] ####################################### ## Set indexes to use into record ## which is treated as a Tcl list var. ####################################### set IDXx [expr {$ENTRYxcolNUM - 1}] set IDXy1 [expr {$ENTRYy1colNUM - 1}] if { "$ENTRYy2colNUM" != "" } { set IDXy2 [expr {$ENTRYy2colNUM - 1}] } ## FOR TESTING: if {0} { puts "" puts "ENTRYxcolNUM: $ENTRYxcolNUM" puts "IDXx: $IDXx" puts "ENTRYy1colNUM: $ENTRYy1colNUM" puts "IDXy1: $IDXy1" } ########################################### ## Start the 'while' loop to read all the ## records in the input file. ########################################### while {! [eof $fid]} { set rec [gets $fid] incr Nrecs ## Remove leading and trailing spaces from the rec. ## In particular, change a line of spaces to a 'null' line. set rec [string trim "$rec"] ## FOR TESTING: # puts "rec: $rec" ########################################### ## Skip any comment recs ('#' in col1) ## and empty (null) recs. ## ## In each case, ## 'continue' to the top of the 'while' loop ## to read the next rec. ########################################### set char1 [ string index "$rec" 0 ] if { "$rec" == "" } { continue } if { "$char1" == "#" } { if {$Nrecs == 1} {set ENTRYtitleMain [string range "$rec" 2 end]} if {$Nrecs == 2} {set ENTRYtitleXaxis [string range "$rec" 2 end]} if {$Nrecs == 3} {set ENTRYtitleY1axis [string range "$rec" 2 end]} continue } ## FOR TESTING: if {0} { puts "" puts "Data rec: $rec" } ###################################################### ## At this point we should be at a data rec. ## Load the 3 array variables according to ## the 2 or 3 specified column numbers. ## ## We treat the record as a Tcl 'list' and ## use 'lindex' to get data from the specified column. ###################################################### incr NXvals set aRxvals($NXvals) [lindex "$rec" $IDXx] set aRy1vals($NXvals) [lindex "$rec" $IDXy1 ] if { "$ENTRYy2colNUM" != "" } { set aRy2vals($NXvals) [lindex "$rec" $IDXy2] } ## FOR TESTING: if {0} { puts "" puts "NXvals: $NXvals" puts "aRxvals($NXvals): $aRxvals($NXvals)" puts "aRy1vals($NXvals): $aRy1vals($NXvals)" } ################################################# ## Proceed to set the MIN and MAX variables: ## XdataMIN XdataMAX Y1dataMIN Y1dataMAX and, ## optionally, Y2dataMIN Y2dataMAX ################################################# if {$NXvals == 1} { set XdataMIN $aRxvals(1) set XdataMAX $aRxvals(1) set Y1dataMIN $aRy1vals(1) set Y1dataMAX $aRy1vals(1) if { "$ENTRYy2colNUM" != "" } { set Y2dataMIN $aRy2vals(1) set Y2dataMAX $aRy2vals(1) } } else { if {$aRxvals($NXvals) < $XdataMIN} {set XdataMIN $aRxvals($NXvals)} if {$aRxvals($NXvals) > $XdataMAX} {set XdataMAX $aRxvals($NXvals)} if {$aRy1vals($NXvals) < $Y1dataMIN} {set Y1dataMIN $aRy1vals($NXvals)} if {$aRy1vals($NXvals) > $Y1dataMAX} {set Y1dataMAX $aRy1vals($NXvals)} if { "$ENTRYy2colNUM" != "" } { if {$aRy2vals($NXvals) < $Y2dataMIN} {set Y2dataMIN $aRy2vals($NXvals)} if {$aRy2vals($NXvals) > $Y2dataMAX} {set Y2dataMAX $aRy2vals($NXvals)} } } ## We are finished processing this data rec. Get the next one. } ## END of while-!eof ## Call proc 'set_lims_ticdist' once to set X-AXIS entry fields ## with 'suggested' values. set tempLISTlims [set_lims_ticdist $XdataMIN $XdataMAX] set ENTRYxaxisMIN [lindex $tempLISTlims 0] set ENTRYxaxisMAX [lindex $tempLISTlims 1] set ENTRYxaxisTICDIST [lindex $tempLISTlims 2] ## FOR TESTING: if {0} { puts "" puts "tempLISTlims: $tempLISTlims" puts "ENTRYxaxisMIN: $ENTRYxaxisMIN" puts "ENTRYxaxisMAX: $ENTRYxaxisMAX" puts "ENTRYxaxisTICDIST: $ENTRYxaxisTICDIST" } ## Call proc 'set_lims_ticdist' again to set Y-AXIS entry fields ## with 'suggested' values. set YdataMIN $Y1dataMIN set YdataMAX $Y1dataMAX if { "$ENTRYy2colNUM" != "" } { if {$Y2dataMIN < $YdataMIN} {set YdataMIN $Y2dataMIN} if {$Y2dataMAX > $YdataMAX} {set YdataMAX $Y2dataMAX} } set tempLISTlims [set_lims_ticdist $YdataMIN $YdataMAX] set ENTRYy1axisMIN [lindex $tempLISTlims 0] set ENTRYy1axisMAX [lindex $tempLISTlims 1] set ENTRYy1axisTICDIST [lindex $tempLISTlims 2] ######################################################### ## Popup a message to indicate the data was read, ## giving number of rows read and the min,max values ## for each column. ## (This could be commented, and just used for testing.) ######################################################### if {1} { set DATAmessage \ " Data has been read. Number of rows of data in the file: $NXvals Total number of lines in the file: $Nrecs Data extremes: X-data in column $ENTRYxcolNUM MIN: $XdataMIN MAX: $XdataMAX Y1-data in column $ENTRYy1colNUM MIN: $Y1dataMIN MAX: $Y1dataMAX" if { "$ENTRYy2colNUM" != "" } { append DATAmessage "\nY2-data in column $ENTRYy2colNUM MIN: $Y2dataMIN MAX: $Y2dataMAX" } ## NOT ENOUGH CONTROL using 'tk_dialog'. Example: Poor line width control. # popup_shortmsg "DataStatistics" "$DATAmessage" popup_msgVarWithScroll .topDataMsg "$DATAmessage" +10+290 } ## END OF 'if {0}' to comment this popup message. } ## END OF PROC 'read_data' ##+##################################################################### ## PROC 'set_lims_ticdist' ##+##################################################################### ## PURPOSE: From a pair of data-min and data-max values, ## this proc returns 'suggested' values for ## axis-min, axis-max, and axis-tic-dist of for an axis ## 'encompassing' those data values. ## ## Arguments: min and max, two numbers --- positive or negative --- ## integer or decimal. min should be less than max, ## even in the case that they are both negative. ## ## In this Tk script to plot from 2 or 3 cols of a data file: ## min and max are set from XdataMIN XdataMAX --- or from ## Y1dataMIN Y1dataMAX Y2dataMIN Y2dataMAX --- just before ## this proc is called. ## ## NOTE: 2 numbers in (min, max) ## 3 numbers out (nice_min, nice_max, nice_ticdist) ## ## CALLED BY: proc 'read_data' ##+##################################################################### proc set_lims_ticdist {min max} { ## FOR TESTING: # return ########################################## ## Get the absolute values of min and max. ########################################## set abs_min [expr {abs($min)}] set abs_max [expr {abs($max)}] #################### ## Set a 'nice min'. #################### if {$min < 0} { set nice_min [set_nice_upper_bound $abs_min] set nice_min [expr {-$nice_min}] } else { set nice_min [set_nice_lower_bound $abs_min] } #################### ## Set a 'nice max'. #################### if {$max < 0} { set nice_max [set_nice_lower_bound $abs_max] set nice_max [expr {-$nice_max}] } else { set nice_max [set_nice_upper_bound $abs_max] } ####################################################### ## We now aim to set 'nice_ticdist'. ## Get the difference between 'nice_max' and 'nice_min' ## --- a positive number. ####################################################### set nicemaxMINUSnicemin [expr {$nice_max - $nice_min}] ####################################################### ## Get a 'nice' upper bound on that positive difference. ####################################################### set upper_nicemaxMINUSnicemin [set_nice_upper_bound $nicemaxMINUSnicemin] ####################################################### ## Set 'nice_ticdist' to be one-tenth of the 'nice' ## upper bound on that positive difference. ####################################################### set nice_ticdist [expr {$upper_nicemaxMINUSnicemin/10.0}] ## FOR TESTING: if {0} { puts "" puts "INTERMEDIATE VALUES from proc 'set_lims_ticdist':" puts "min: $min" puts "max: $max" puts "nice_min: $nice_min" puts "nice_max: $nice_max" puts "nicemaxMINUSnicemin: $nicemaxMINUSnicemin" puts "upper_nicemaxMINUSnicemin: $upper_nicemaxMINUSnicemin" puts "nice_ticdist: $nice_ticdist" } ########################################################### ## Adjust 'nice_min' so that 'nice_ticdist' draws tic-marks ## that fits nicely between 'nice_min' and 'nice_max'. ########################################################### set nice_min [expr {$nice_max - $upper_nicemaxMINUSnicemin}] ## FOR TESTING: if {0} { puts "" puts "RETURN VALUES from proc 'set_lims_ticdist':" puts "nice_min: $nice_min" puts "nice_max: $nice_max" puts "nice_ticdist: $nice_ticdist" } ########################################################### ## The number of decimal places in the tic-mark-distance ## controls the number of decimal places in the tic-mark-labels. ## ## If the suggested tic-mark-distance ends in '.0', ## the user probably wants the tic-mark-labels to drop ## the decimal point. ########################################################### set TEMPdecimal [lindex [split "$nice_ticdist" "."] 1] if {"$TEMPdecimal" == "0"} {set nice_ticdist [format_deciPlaces $nice_ticdist 0]} return "$nice_min $nice_max $nice_ticdist" } ## END OF PROC 'set_lims_ticdist' ##+##################################################################### ## PROC 'set_nice_upper_bound' ##+##################################################################### ## PURPOSE: For a given positive number, this proc returns ## a number greater than that number --- but a 'nice' ## number close to the input number, based on a power of 10. ## ## Argument: posnum (a positive number --- integer or decimal) ## ## CALLED BY: proc 'set_lims_ticdist' ##+##################################################################### proc set_nice_upper_bound {posnum} { ## Get log-base-10 of posnum. set exp10 [expr {log10($posnum)}] ## Get the smallest integer greater than log-base-10 of posnum. set ceil_exp10 [expr {ceil($exp10)}] ## Use that integer as an exponent base 10 ## --- to get a first estimate of 'uppernum'. set uppernum [expr {pow(10,$ceil_exp10)}] ## Keep decreasing 'uppernum' by 50% (1/2) and checking if ## it is still greater than 'posnum'. ## ## When a halving results in a number less than 'posnum', ## break out of the 'while' loop --- ## leaving us with a desirable value for 'uppernum'. ## ## NOTE: We could try a 20% (1/5) or 10% (1/10) decrease ## and probably still get nice 'base10' numbers. while 1 { set smaller_uppernum [expr {0.5 * $uppernum}] if {$smaller_uppernum > $posnum} { set uppernum $smaller_uppernum } else { break } } ## END OF 'while' LOOP ## FOR TESTING: if {0} { puts "posnum: $posnum" puts "" puts "exp10: $exp10" puts "ceil_exp10: $ceil_exp10" puts "uppernum: $uppernum" } return $uppernum } ## END OF PROC 'set_nice_upper_bound' ##+##################################################################### ## PROC 'set_nice_lower_bound' ##+##################################################################### ## PURPOSE: For a given positive number, this proc returns ## a number less than that number --- but a 'nice' ## number close to the input number, based on a power of 10. ## ## Argument: posnum (a positive number --- integer or decimal) ## ## CALLED BY: proc 'set_lims_ticdist' ##+##################################################################### proc set_nice_lower_bound {posnum} { ## Get log-base-10 of posnum. set exp10 [expr {log10($posnum)}] ## Get the greatest integer less than log-base-10 of posnum. set floor_exp10 [expr {floor($exp10)}] ## Use that integer as an exponent base 10 ## --- to get a first estimate of 'lowernum'. set lowernum [expr {pow(10,$floor_exp10)}] ## Keep increasing 'lowernum' by 50% (1/2) and checking if it is still ## less than 'posnum'. ## ## When the increasing results in a number greater than 'posnum', ## break out of the 'while' loop --- ## leaving us with a desirable value for 'lowernum'. ## ## NOTE: We could try a 20% (1/5) or 10% (1/10) increase ## and probably still get nice 'base10' numbers. while 1 { set bigger_lowernum [expr {1.5 * $lowernum}] if {$bigger_lowernum < $posnum} { set lowernum $bigger_lowernum } else { break } } ## END OF 'while' LOOP ## FOR TESTING: if {0} { puts "posnum: $posnum" puts "" puts "exp10: $exp10" puts "floor_exp10: $floor_exp10" puts "lowernum: $lowernum" } return $lowernum } ## END OF PROC 'set_nice_lower_bound' ##+##################################################################### ## PROC 'update_plot' ##+##################################################################### ## PURPOSE: ## This procedure is invoked to (re)create the canvas plot items ## --- lines (axes, tic-marks, data-lines/points) and ## text (plot title, axis titles, tic-mark labels) --- using the ## current GUI entries -- and the data currently read into X,Y1,Y2 ## arrays from a data file. ## Uses 'create line' to create axes and tic-marks. ## Uses 'create text' for titles and tic-mark labels. ## Uses 'create line' to connect points in the plot ## and 'create oval' to mark data points. ## ## Arguments: none (all global variables ## ## CALLED BY: the 'UpdatePlot' button --- and the 'downsize_canvas' ## and 'upsize_canvas' procs --- and at the bottom of this ## script in the 'Additional GUI Initialization' section. ##+##################################################################### proc update_plot {} { ## FOR TESTING: # return ## We need the title entries: global ENTRYtitleMain ENTRYtitleXaxis ENTRYtitleY1axis ## We need the column-number entries: global ENTRYxcolNUM ENTRYy1colNUM ENTRYy2colNUM ## Make sure the colNUM vars have no leading or trailing spaces. ## In particular, make sure "$ENTRYy2colNUM" == "" (null) if it is left blank. set ENTRYxcolNUM [string trim $ENTRYxcolNUM] set ENTRYy1colNUM [string trim $ENTRYy1colNUM] set ENTRYy2colNUM [string trim $ENTRYy2colNUM] ## We need the axis numeric entries --- which may contain 'suggested' ## values from within proc 'read_data': global ENTRYxaxisMIN ENTRYxaxisMAX ENTRYxaxisTICDIST \ ENTRYy1axisMIN ENTRYy1axisMAX ENTRYy1axisTICDIST ## We need the checkbutton variables: global dataLines0or1 dataPoints0or1 plotBorder0or1 ## We need the array data variables from proc 'read_data': global aRxvals aRy1vals aRy2vals NXvals ENTRYfilename if { "$ENTRYfilename" == "" } { set ERRmessage "Filename was not specified." set ERRtitle "Input Err" popup_shortmsg "$ERRtitle" "$ERRmessage" return } if { ![array exists aRxvals] } { set ERRmessage "Data arrays not loaded." set ERRtitle "Input Err" popup_shortmsg "$ERRtitle" "$ERRmessage" return } ## We need the margin-pixels variables from proc 'set_margins': global MARGNleftPx MARGNrightPx MARGNtopPx MARGNbotPx ## CharHeightPx is set where win minsize is set, at top of script. global CharHeightPx ## We need the color variables from proc 'set_plotColors'. global lineCOLORhex textCOLORregHEX textCOLORselHEX pointCOLORregHEX ## We need line-width-pixels variable which is set at the bottom ## of this script in the 'Additional GUI Initialization' section. global lineWIDTHpx ## We may need to share the current canvas dimensions with other ## procs --- such as 'draw_border'. global CURcanWidthPx CURcanHeightPx ########################################################## ## Make sure we are using the current canvas dimensions to ## place items on the plot canvas. ########################################################## set CURcanWidthPx [winfo width .fRlow.fRplot.can] set CURcanHeightPx [winfo height .fRlow.fRplot.can] ########################################################## ## Set some margin vars to be used to set endpoints of ## x and y axes, etc. --- in pixel units. ## (NOTE: The contents of the 'set_margins' proc may ## need to take into account, i.e. use, various ## axis-location-related 'if' statements ## in this 'update_plot' code --- further below.) ########################################################## set_margins ##################################################################### ## CLEAR THE CANVAS WIDGET in preparation for redraw. ##################################################################### ## Rather drastic: # .fRlow.fRplot.can delete all ## To avoid deletion of images/logo: .fRlow.fRplot.can delete TAGtext .fRlow.fRplot.can delete TAGlines .fRlow.fRplot.can delete TAGdataPoints .fRlow.fRplot.can delete TAGborder ## where ## TAGtext is a tag for the main title, x-and-y axis-titles, ## and ticmark labels. ## TAGlines is a tag for the axes and tic-marks and ## data-lines (if any). ## TAGdataPoints is a tag for the data points (filled-ovals), if any. ## TAGborder is a tag for the drawn border (lines and arcs), if any. ########################################################### ## PUT MAIN TITLE NEAR TOP OF THE PLOT-CANVAS, ## anchored to the left side of the plot area. ## NOTE: ## The user can put leading spaces in the plot-title entry ## field to adjust the title to the right. ## In addition, the user can drag the title. ########################################################### # set xPX 10 set xPX $MARGNleftPx ## Below we are placing the text with '-anchor nw'. ## ## We set the northwest corner of the plot-title text ## just a few pixels below the top of the canvas ## and at the left side of the 'plot rectangle' set ## in the 'set_margins' proc. set yPX 4 .fRlow.fRplot.can create text \ $xPX $yPX \ -anchor nw \ -text "$ENTRYtitleMain" \ -font fontTEMP_label \ -fill "#$textCOLORregHEX" \ -tag {TAGmoveable TAGtext TAGtitles} # -justify center \ ########################################################### ## SET XaxisMINpx,XaxisMAXpx,YaxisTOPpx,YaxisBOTpx ## (in canvas pixels) for global use for end-points of axes. ########################################################### ## YaxisTOPpx & YaxisBOTpx refer to locations at the top and ## bottom of the canvas area. ## XaxisMINpx & XaxisMAXpx refer to locations on the left and ## right of the canvas area. ########################################################### set XaxisMINpx $MARGNleftPx set XaxisMAXpx [expr { $CURcanWidthPx - $MARGNrightPx } ] set YaxisTOPpx $MARGNtopPx set YaxisBOTpx [expr { $CURcanHeightPx - $MARGNbotPx }] ########################################################### ## We put the Y-axis title near the top of the plot, ## anchored to the left side of the plot area. ## NOTE: The user can use leading-spaces in the yaxis-title ## entry field to adjust the title to the right. ########################################################### # set xPX [expr { $MARGNleftPx + 4 } ] set xPX $MARGNleftPx ## Note that we are placing the text with '-anchor nw' below. ## ## We set the northwest corner of the yaxis-title text ## at the top of the left side of the 'plot rectangle' ## set in the 'set_margins' proc. # set yPX [expr { $MARGNtopPx - 3 }] set yPX $MARGNtopPx .fRlow.fRplot.can create text \ $xPX $yPX \ -anchor nw \ -justify left \ -text "$ENTRYtitleY1axis" \ -font fontTEMP_label \ -fill "#$textCOLORregHEX" \ -tags {TAGmoveable TAGtext TAGtitles} ########################################################### ## We put the X-axis title near the bottom of the plot, ## anchored to the left side of the plot area. ## NOTE: The user can use leading-spaces in the xaxis-title ## entry field to adjust the title to the right. ########################################################### # set xPX [expr { $MARGNleftPx + 4 } ] set xPX $MARGNleftPx ## Note that we are placing the text with '-anchor sw' below. ## ## We set the southwest corner of the xaxis-title text ## just a few pixels above the bottom of the canvas ## and at the left side of the 'plot rectangle' set ## in the 'set_margins' proc. # set yPX $YaxisBOTpx set yPX [expr { $CURcanHeightPx - 5 }] .fRlow.fRplot.can create text \ $xPX $yPX \ -anchor sw \ -justify left \ -text "$ENTRYtitleXaxis" \ -font fontTEMP_label \ -fill "#$textCOLORregHEX" \ -tags {TAGmoveable TAGtext TAGtitles} ##################################################### ## SETUP FACTORS THAT WILL BE USED TO ## CONVERT DATA VALS TO PIXEL COORDS. ## Units: Pixels per 'world coordinate' units. ## Data type: floating point (not integer) ##################################################### set PX2UNITSx [expr { double( $XaxisMAXpx - $XaxisMINpx ) / ( $ENTRYxaxisMAX - $ENTRYxaxisMIN ) } ] set PX2UNITSy [expr { double( $YaxisBOTpx - $YaxisTOPpx ) / ( $ENTRYy1axisMAX - $ENTRYy1axisMIN ) } ] ## FOR TESTING: # puts "PX2UNITSx: $PX2UNITSx" # puts "PX2UNITSy: $PX2UNITSy" ######################################################################### ## SET X-AXIS VERTICAL LOCATION (IN PIXELS) --- in XAXISyPX. ## If ENTRYy1axisMAX & ENTRYy1axisMIN (in 'world coordinates') straddle 0, ## set X-axis vert-loc according to the location of y=0 IN PIXELS ## --- else use YaxisBOTpx. ## ## NOTE: These kinds of checks will probably need to be ## put in the 'set_margins' proc. ######################################################################### set XAXISyPX $YaxisBOTpx if { $ENTRYy1axisMAX > 0 && $ENTRYy1axisMIN < 0 } { set XAXISyPX [ expr { $YaxisBOTpx + int( $PX2UNITSy * $ENTRYy1axisMIN) } ] } ############################################################## ## SET Y-axis horizontal location (in pixels) --- in YAXISxPX. ## If ENTRYxaxisMAX & ENTRYxaxisMIN straddle 0, ## set Y-axis horiz-loc according to location of x=0 IN PIXELS ## --- else use XaxisMINpx. ## ## NOTE: These kinds of checks will probably need to be ## put in the 'set_margins' proc. ############################################################## set YAXISxPX $XaxisMINpx if { $ENTRYxaxisMAX > 0 && $ENTRYxaxisMIN < 0 } { set YAXISxPX [ expr { $XaxisMINpx - int($PX2UNITSx * $ENTRYxaxisMIN) } ] } ########################################################### ## DRAW X-AXIS ON THE PLOT-CANVAS, ## in the 'plot rectangle'. ########################################################### set x0PX $XaxisMINpx set y0PX $XAXISyPX set x1PX $XaxisMAXpx set y1PX $XAXISyPX .fRlow.fRplot.can create line \ $x0PX $y0PX \ $x1PX $y1PX \ -fill black \ -width $lineWIDTHpx -capstyle round \ -fill "#$lineCOLORhex" -tags TAGlines ########################################################### ## DRAW enough X-AXIS TIC-MARKS to fit between X min & max ## --- and label them. ## ## These hard-coded x-axis tic-mark offsets may need to be ## set as a small percentage of the canvas height. ########################################################### # set ticOFFSETpx 15 set ticOFFSETpx 5 set xTicTopPx [expr { $y0PX - $ticOFFSETpx } ] set xTicBotPx [expr { $y0PX + $ticOFFSETpx } ] set Nxtics [expr { int ( ($ENTRYxaxisMAX - $ENTRYxaxisMIN) / $ENTRYxaxisTICDIST) } ] set xTicIncrWrld $ENTRYxaxisTICDIST set xTicIncrPx [expr { $PX2UNITSx * $xTicIncrWrld } ] set xDeciPlaces [get_deciPlaces "$xTicIncrWrld"] for {set i 0} {$i <= $Nxtics} {incr i} { set xPX [expr {$x0PX + ($i * $xTicIncrPx)}] .fRlow.fRplot.can create line \ $xPX $y0PX \ $xPX $xTicTopPx \ -width $lineWIDTHpx -capstyle round \ -fill "#$lineCOLORhex" -tags TAGlines set ticText [ format_deciPlaces [expr {$ENTRYxaxisMIN + ($xTicIncrWrld * $i)}] $xDeciPlaces ] .fRlow.fRplot.can create text \ $xPX $xTicBotPx \ -text $ticText \ -anchor n \ -font fontTEMP_SMALL_label \ -fill "#$textCOLORregHEX" \ -tags {TAGmoveable TAGtext TAGtitles} } ## END OF LOOP for {set i 0} {$i <= $Nxtics} {incr i} ############################################################ ## DRAW Y-AXIS ON THE PLOT CANVAS, ## in the 'plot rectangle'. ############################################################ set x0PX $YAXISxPX set y0PX $YaxisBOTpx set x1PX $YAXISxPX set y1PX $YaxisTOPpx .fRlow.fRplot.can create line \ $x0PX $y0PX \ $x1PX $y1PX \ -fill black \ -width $lineWIDTHpx -capstyle round \ -fill "#$lineCOLORhex" -tags TAGlines ########################################################### ## DRAW enough Y-AXIS TIC-MARKS to fit between Y min & max ## --- and label them. ## ## These hard-coded y-axis tic-mark offsets may need to be ## set as a small percentage of the canvas width. ########################################################### # set ticOFFSETpx 15 set ticOFFSETpx 5 set yTicLeftPx [expr { $x0PX - $ticOFFSETpx } ] set yTicRightPx [expr { $x0PX + $ticOFFSETpx } ] set Nytics [expr { int ( ($ENTRYy1axisMAX - $ENTRYy1axisMIN) / $ENTRYy1axisTICDIST) } ] set yTicIncrWrld $ENTRYy1axisTICDIST set yTicIncrPx [expr { $PX2UNITSy * $yTicIncrWrld } ] set yDeciPlaces [get_deciPlaces "$yTicIncrWrld"] for {set i 0} {$i <= $Nytics} {incr i} { set yPX [expr {$y0PX - ($i*$yTicIncrPx)}] .fRlow.fRplot.can create line \ $x0PX $yPX \ $yTicRightPx $yPX \ -width $lineWIDTHpx -capstyle round \ -fill "#$lineCOLORhex" -tags TAGlines set ticText [ format_deciPlaces [expr {$ENTRYy1axisMIN + ($yTicIncrWrld * $i)}] $yDeciPlaces ] .fRlow.fRplot.can create text \ $yTicLeftPx $yPX \ -text $ticText \ -anchor e \ -justify right \ -font fontTEMP_SMALL_label \ -fill "#$textCOLORregHEX" \ -tags {TAGmoveable TAGtext TAGtitles} } ## END OF LOOP for {set i 0} {$i <= $Nytics} {incr i} ########################################################### ## If data lines are requested, DRAW DATA LINES. ## But first set the xprev,y1prev,y2prev coords (in pixels). ########################################################### if {$dataLines0or1 == 1} { set xprevPX [expr {$XaxisMINpx + ( $PX2UNITSx * ( $aRxvals(1) - $ENTRYxaxisMIN ) ) } ] set y1prevPX [expr {$YaxisBOTpx - ( $PX2UNITSy * ( $aRy1vals(1) - $ENTRYy1axisMIN ) ) } ] if {"$ENTRYy2colNUM" != ""} { set y2prevPX [expr {$YaxisBOTpx - ( $PX2UNITSy * ( $aRy2vals(1) - $ENTRYy1axisMIN ) ) } ] } for {set i 2} {$i <= $NXvals} {incr i} { ## FOR TESTING: # puts "ENTRYxaxisMIN: $ENTRYxaxisMIN" # puts "Xval(i): $aRxvals($i)" # puts "i: $i" ## Draw the Y1 data lines. set xPX [expr {$XaxisMINpx + ( $PX2UNITSx * ( $aRxvals($i) - $ENTRYxaxisMIN ) ) } ] set yPX [expr {$YaxisBOTpx - ( $PX2UNITSy * ( $aRy1vals($i) - $ENTRYy1axisMIN ) ) } ] .fRlow.fRplot.can create line \ $xprevPX $y1prevPX \ $xPX $yPX \ -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags {TAGdataLines TAGlines} set y1prevPX $yPX ## Draw the Y2 data lines, if requested. if {"$ENTRYy2colNUM" != ""} { set yPX [expr {$YaxisBOTpx - ( $PX2UNITSy * ( $aRy2vals($i) - $ENTRYy1axisMIN ) ) } ] .fRlow.fRplot.can create line \ $xprevPX $y2prevPX \ $xPX $yPX \ -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags {TAGdataLines TAGlines} set y2prevPX $yPX } set xprevPX $xPX } ## END OF LOOP for {set i 2} {$i <= $NXvals} {incr i} } ## END OF '$dataLines0or1 == 1' section ##################################################################### ## If data points are requested, DRAW DATA POINTS. ##################################################################### if {$dataPoints0or1 == 1} { ##################################################### ## SET RADIUS OF DATA POINT MARKERS. ## We use circles for y1 data, using 'create oval'. ## We use squares for y2 data, using 'create rect'. ##################################################### set markerRadius 3 ############################################################# ## LOOP THRU X,Y1,Y2 DATA LISTS, CREATING DATA-POINT-MARKERS. ############################################################# for {set i 1} {$i <= $NXvals} {incr i} { ## FOR TESTING: # puts "ENTRYxaxisMIN: $ENTRYxaxisMIN" # puts "Xval(i): $aRxvals($i)" # puts "i: $i" ## Draw the Y1 data-points. set xPX [expr {$XaxisMINpx + ( $PX2UNITSx * ( $aRxvals($i) - $ENTRYxaxisMIN ) ) } ] set yPX [expr {$YaxisBOTpx - ( $PX2UNITSy * ( $aRy1vals($i) - $ENTRYy1axisMIN ) ) } ] .fRlow.fRplot.can create oval \ [expr {$xPX - $markerRadius}] [expr {$yPX - $markerRadius}] \ [expr {$xPX + $markerRadius}] [expr {$yPX + $markerRadius}] \ -width 1 -outline "#$lineCOLORhex" -fill "#$pointCOLORregHEX" \ -tags TAGdataPoints ## Draw the Y2 data-points, if requested. if {"$ENTRYy2colNUM" != ""} { set yPX [expr {$YaxisBOTpx - ( $PX2UNITSy * ( $aRy2vals($i) - $ENTRYy1axisMIN ) ) } ] .fRlow.fRplot.can create rect \ [expr {$xPX - $markerRadius}] [expr {$yPX - $markerRadius}] \ [expr {$xPX + $markerRadius}] [expr {$yPX + $markerRadius}] \ -width 1 -outline "#$lineCOLORhex" -fill "#$pointCOLORregHEX" \ -tags TAGdataPoints } } ## END OF LOOP for {set i 1} {$i <= $NXvals} {incr i} } ## END OF '$dataPoints0or1 == 1' section ################################################################### ## If 'PlotBorder' checkbutton is ON, draw border. ################################################################### if {$plotBorder0or1 == 1} {draw_border} } ## END OF PROC 'update_plot' ##+##################################################################### ## PROC 'set_margins' ##+##################################################################### ## PURPOSE: ## Sets 4 'MARGN' variables that can be used to consistently set the ## limits of the 'plot rectangle' within the canvas widget. ## ## The 'plot rectangle' is the area on the canvas to which we want ## to confine the plotting of the data points. ## ## (Often the X-axis will be drawn at the bottom of the 'plot rectangle' ## and the Y-axis will be drawn at the left of the 'plot rectangle' ## --- BUT NOT ALWAYS.) ## ## The 4 'MARGN' values, in pixels, are meant to be: ## ## MARGNtopPx distance between TOP side of canvas and ## TOP side of 'plot rectangle' ## MARGNleftPx distance between LEFT side of canvas and ## LEFT side of 'plot rectangle' ## MARGNrightPx distance between RIGHT side of canvas and ## RIGHT side of 'plot rectangle' ## MARGNbotPx distance between BOTTOM side of canvas and ## BOTTOM side of 'plot rectangle' ## ## (Whenever the canvas is resized, these 'MARGN' values can be ## used to resize the 'central' plot rectangle.) ## #################################### ## SPECIFICS OF THE 'plot rectangle': (top,left,right,bottom) ## ## The 'plot rectangle' is always meant to allow some margin at ## the TOP of the canvas for a plot title --- without data points ## going into the plot title. ## ## The 'plot rectangle' is typically meant to allow some margin ## at the LEFT of the canvas, for some tic-mark labels on the ## left of a vertical Y-axis --- especially when that Y-axis is ## drawn at the left of the 'plot rectangle'. ## ## The 'plot rectangle' is typically meant to allow some margin ## at the RIGHT of the canvas, for some tic-mark labels on the ## right of a vertical Y-axis --- especially when the ## Y-axis is drawn at the right of the 'plot rectangle' ## (for example at x=0 when that is the right end of the x-axis). ## ## The 'plot rectangle' is typically meant to allow some margin ## at the BOTTOM of the canvas, for some tic-mark labels on the ## bottom side of a horizontal X-axis --- especially when the ## X is drawn at the bottom of the 'plot rectangle'. ## ######################################### ## MORE SPECIFICS OF THE 'plot rectangle': (dimensions) ## ## The 'plot rectangle', horizontally, starts at one end ## of the x-axis and goes to the other end of the x-axis ## --- in other words, the horizontal dimension of the ## 'plot rectangle' is supposed to be exactly equal to ## the length of the x-axis. ## ## The 'plot rectangle', vertically, starts at one end ## of the y-axis and goes to the other end of the y-axis ## --- in other words, the vertical dimension of the ## 'plot rectangle' is supposed to be exactly equal to ## the length of the y-axis. ## ## In short, the 'plot rectangle' is the smallest rectangle ## containing the drawing of the x and y axes. ## ############################################# ## USING FONT SIZES IN SETTING 'MARGN' VALUES: ## ## The plot-titles, axis-titles, and tic-mark labels generally ## lie outside the 'plot rectangle', so these 4 'MARGN' values ## should take those items into account (allow room for them). ## (Since the plot-titles, axis-titles, and tic-mark labels in ## this app can be moved, we have a little lee-way in setting ## the margins --- but not much.) ## ## Because of the text items in the margins, the choice of fonts ## (set at top of script) can/should be used to set these 4 ## 'MARGN' values (in pixels). ## ## Note that we set these margins in 'absolute' pixel units ## rather than as 'proportionality factors' to be applied ## to the canvas dimensions. ## ## These 4 'margin values' will be used to set variables that ## will be used to define the ending points (in pixel units) ## of the plot axes within the plot canvas, ## say XaxisMINpx,XaxisMAXpx,YaxisTOPpx,YaxisBOTpx (in pixels). ##+######################################################## ## Example use: ## set XaxisMINpx $MARGNleftPx ## set XaxisMAXpx [expr { $CURCURcanWidthPx - $MARGNrightPx } ] ## set YaxisTOPpx $MARGNtopPx ## set YaxisBOTpx [expr { $CURcanHeightPx - $MARGNbotPx }] ##+######################################################## ## CALLED BY: the 'update_plot' proc ##+######################################################## proc set_margins {} { ## The 'MARGN' settings below assume a Y axis at the left ## of the 'plot rectangle' and an X-axis at the bottom. ## ## Eventually we will want to put in logic, like some in ## the 'update_plot' proc, to take into account situations ## like X-axis above the bottom --- even at the top --- ## and Y axis in the middle or on the right. ## The variable 'CharHeightPx' was set where the GUI window ## minsize was set, near the top of this script. Specifically: ## set CharHeightPx [font metrics fontTEMP_varwidth -linespace] ## ## But we did not set a 'CharWidthPx' variable. ## We do that here. global CharHeightPx \ MARGNleftPx MARGNrightPx MARGNtopPx MARGNbotPx set CharWidthPx [font measure fontTEMP_varwidth "W"] ## NOTE: We are setting margins (in pixels) according ## to the font being used for text on the plot --- ## irrespective of the current dimensions of the canvas. ## I.e. we are not using percentages of canvas dimensions. set MARGNleftPx [expr {4 * $CharWidthPx}] set MARGNrightPx [expr {2 * $CharWidthPx}] set MARGNtopPx [expr {2 * $CharHeightPx}] set MARGNbotPx [expr {3 * $CharHeightPx}] } ## END OF PROC 'set_margins' ##+##################################################################### ## 'get_deciPlaces' PROCEDURE ##+##################################################################### ## PURPOSE: Get the number of decimal places in a number string. ## ## Argument: a number string like 10.0 or 10 or 0.25 ## ## CALLED BY: the 'update_plot' proc ##+##################################################################### proc get_deciPlaces {input} { set deciPlaces [string length [lindex [split "$input" "."] 1]] return "$deciPlaces" } ## END OF PROC 'get_deciPlaces' ##+##################################################################### ## 'format_deciPlaces' PROCEDURE ##+##################################################################### ## PURPOSE: Format a number string to have a given number of ## decimal places. ## ## Arguments: a number string AND an integer specifying the ## number of decimal places ## ## CALLED BY: the 'update_plot' proc ##+##################################################################### proc format_deciPlaces {input places} { set FORMAT4number "%0.${places}f" set output [format "$FORMAT4number" $input] return "$output" } ## END OF PROC 'format_deciPlaces' ##+##################################################################### ## PROC 'itemSelect' ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked when the mouse is pressed over a ## canvas item. It sets up a state to allow the point to be dragged ## by marking the item with the tag 'TAGselected'. ## ## Arguments: ## w - The canvas window. ## x, y - The coordinates of the mouse press. ## ## CALLED BY: a binding on a window 'w', which in this ## application is always the canvas window/widget: ## .fRlow.fRplot.can bind ... ##+##################################################################### set lastXsel 0 set lastYsel 0 proc itemSelect {w x y} { global lastXsel lastYsel ## Delete the tag 'TAGselected' from any 'TAGselected' items. $w dtag TAGselected ## Add the tag 'TAGselected' to the currently selected item. $w addtag TAGselected withtag current $w raise current set lastXsel $x set lastYsel $y } ## END OF PROC 'itemSelect' ##+##################################################################### ## PROC 'itemMove' ##+##################################################################### ## ## PURPOSE: ## This procedure is invoked during mouse motion events. ## It drags the currently selected item, which is marked by ## the tag 'TAGselected'. ## ## Arguments: ## w - The canvas window. ## x, y - The coordinates of the mouse. ## ## CALLED BY: a binding on a window 'w', which in this ## application is always the canvas window/widget: ## .fRlow.fRplot.can bind ... ##+##################################################################### proc itemMove {w x y} { global lastXsel lastYsel $w move TAGselected [expr {$x-$lastXsel}] [expr {$y-$lastYsel}] set lastXsel $x set lastYsel $y } ## END OF PROC 'itemMove' ##+##################################################################### ## PROC 'draw_border' ##+##################################################################### ## PURPOSE: To draw a border around the edge of the canvas. ## ## METHOD: ## Uses 'create line' to draw 4 straight lines and uses ## 'create arc' to draw 4 rounded corners. ## ## Arguments: none ## ## CALLED BY: button .fRbuttons.buttBorder ##+##################################################################### proc draw_border {} { ## Get current canvas dimensions, set in 'update_plot' proc. global CURcanWidthPx CURcanHeightPx lineCOLORhex lineWIDTHpx ################################################# ## Set border limits. ################################################# set BDxminPx [expr { int(0.01 * $CURcanWidthPx) } ] set BDxmaxPx [expr { int(0.99 * $CURcanWidthPx) } ] set BDytopPx [expr { int(0.01 * $CURcanHeightPx) }] set BDybotPx [expr { int(0.99 * $CURcanHeightPx) }] ################################################# ## Set corner arc radius. ################################################# set minDimPx $CURcanWidthPx if { $minDimPx > $CURcanHeightPx } { set minDim $CURcanHeightPx } set BDradPx [expr { 0.03 * $minDimPx } ] ################################################# ## Draw the four border lines (left,right,top,bot). ################################################# .fRlow.fRplot.can create line \ $BDxminPx [expr { $BDytopPx + $BDradPx } ] \ $BDxminPx [expr { $BDybotPx - $BDradPx } ] \ -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags TAGborder .fRlow.fRplot.can create line \ $BDxmaxPx [expr { $BDytopPx + $BDradPx } ] \ $BDxmaxPx [expr { $BDybotPx - $BDradPx } ] \ -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags TAGborder .fRlow.fRplot.can create line \ [expr { $BDxminPx + $BDradPx } ] $BDytopPx \ [expr { $BDxmaxPx - $BDradPx } ] $BDytopPx \ -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags TAGborder .fRlow.fRplot.can create line \ [expr { $BDxminPx + $BDradPx } ] $BDybotPx \ [expr { $BDxmaxPx - $BDradPx } ] $BDybotPx \ -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags TAGborder ################################################# ## Draw the four border arcs (UL,UR,LR,LL). ################################################# .fRlow.fRplot.can create arc \ $BDxminPx $BDytopPx \ [expr { $BDxminPx + 2.0*$BDradPx } ] [expr { $BDytopPx + 2.0*$BDradPx } ] \ -start 90 \ -extent 90 \ -style arc -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags TAGborder .fRlow.fRplot.can create arc \ $BDxmaxPx $BDytopPx \ [expr { $BDxmaxPx - 2.0*$BDradPx } ] [expr { $BDytopPx + 2.0*$BDradPx } ] \ -start 0 \ -extent 90 \ -style arc -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags TAGborder .fRlow.fRplot.can create arc \ $BDxmaxPx $BDybotPx \ [expr { $BDxmaxPx - 2.0*$BDradPx } ] [expr { $BDybotPx - 2.0*$BDradPx } ] \ -start 270 \ -extent 90 \ -style arc -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags TAGborder .fRlow.fRplot.can create arc \ $BDxminPx $BDybotPx \ [expr { $BDxminPx + 2.0*$BDradPx } ] [expr { $BDybotPx - 2.0*$BDradPx } ] \ -start 180 \ -extent 90 \ -style arc -width $lineWIDTHpx -fill "#$lineCOLORhex" \ -tags TAGborder } ## END OF PROC 'draw_border' ##+##################################################################### ## PROC 'getSet_canvasColor' ##+##################################################################### ## PURPOSE: Gets an RGB hex-color value (via a GUI with 3 RGB slider bars). ## The user uses the RGB color-selector GUI to select a color. ## ## Then sets the canvas 'bg' (background) to that color. ## ## Calls proc 'set_plotColors' to set some variables to hold ## colors that are in contrast with the canvas background color. ## ## NOTE: We always expect hex-color variables to hold the color in a ## form like 'ffffff' rather than '#ffffff'. We will supply the ## '#' wherever we use the hex-color variable. ## ## Arguments: none ## ## CALLED BY: the 'CanvasColor' button ##+##################################################################### proc getSet_canvasColor {} { global CANr255 CANg255 CANb255 CANrgbHEX RGBcolorSelectorScript ## FOR TESTING: # puts "CANr255: $CANr255" # puts "CANg255: $CANg255" # puts "CANb255: $CANb255" set TEMPrgb [ exec $RGBcolorSelectorScript $CANr255 $CANg255 $CANb255] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } ## Script 'sho_colorvals_via_sliders3rgb.tk' puts out 4 strings ## of the form 'r255 g255 b255 hexRGB' where the ## first 3 are integers (0-255) and the last is a 6-char hex RGB code. scan $TEMPrgb "%s %s %s %s" CANr255 CANg255 CANb255 CANrgbHEX ## FOR TESTING: # puts "PROC 'getSet_canvasColor' :" # puts "TEMPrgb: $TEMPrgb" # puts "CANr255: $CANr255" # puts "CANg255: $CANg255" # puts "CANb255: $CANb255" ## Set the canvas background color and 'related' plot colors. set_plotColors "$CANrgbHEX" ## Update the plot to apply the new colors. # update update_plot ## Update the background and foreground colors on the ## background-color button. update_cancolor_button } ## END OF PROC 'getSet_canvasColor' ##+##################################################################### ## PROC 'update_cancolor_button' ##+##################################################################### ## PURPOSE: ## This procedure is invoked to update the color (and, optionally, the ## text) on the canvas background-color button --- ## to show current canvas color (and, optionally, hex value of the color) ## as the background-color on the canvas-color button. ## ## This proc sets the background color of the 'CanvasColor' button to the ## canvas background color as set in the 'getSet_canvasColor' proc ## --- and sets foreground color to a suitable black or white color, ## so that the label text is readable. ## ## Arguments: global color vars ## ## CALLED BY: proc 'getSet_canvasColor' ## and the additional-GUI-initialization section at ## the bottom of this script. ##+##################################################################### proc update_cancolor_button {} { global aRtext CANr255 CANg255 CANb255 CANrgbHEX ## Set background color on the 'CanvasColor' button and ## set the foreground color of the button. .fRbuttons.buttCanvasColor configure -bg "#$CANrgbHEX" set sumCOLOR [expr {$CANr255 + $CANg255 + $CANb255}] if {$sumCOLOR > 300} { .fRbuttons.buttCanvasColor configure -fg "#000000" } else { .fRbuttons.buttCanvasColor configure -fg "#f0f0f0" } ## We could also put the background color in the text on the button ## --- if we did not mind a 2-char high button. # .fRbuttons.buttCanvasColor configure -text "$aRtext(buttonCANCOLOR) # $CANrgbHEX" } ## END OF PROC 'update_cancolor_button' ##+##################################################################### ## PROC 'set_plotColors' ##+##################################################################### ## PURPOSE: ## For a given hex RGB color, set the canvas background color ## and set 'related' (global) color variables: ## lineCOLORhex ## textCOLORregHEX ## textCOLORselHEX ## 'reg' = regular ; 'sel' = selected ## ## Argument: a hex RGB color string, like 'ff9933' ## ## CALLED BY: proc 'getSet_canvasColor' and at bottom of this script, in ## the 'Additional GUI Initialization' section, to ## initialize canvas color and related colors. ##+##################################################################### proc set_plotColors {hexRGBcolor} { global lineCOLORhex textCOLORregHEX textCOLORselHEX pointCOLORregHEX ############################################ ## Apply the color to the canvas background. ############################################ .fRlow.fRplot.can configure -bg "#$hexRGBcolor" ##+########################################### ## SET COLORS FOR CANVAS LINE and TEXT ITEMS. ## 'reg' = regular ; 'sel' = selected ## ## For line and text colors, we can use a choice ## of procs --- such as ## 'invert_hexRGBcolor' or ## 'blackORwhite_farthestFromHexRGBcolor' ## ## For the 'sel' text color, we want a color ## distinct from the 'reg' color. We use an ## 'augment_hexRGBcolor' proc. ##+########################################### # set lineCOLORhex [invert_hexRGBcolor "$hexRGBcolor"] set lineCOLORhex [blackORwhite_farthestFromHexRGBcolor "$hexRGBcolor"] # set textCOLORregHEX [invert_hexRGBcolor "$hexRGBcolor"] set textCOLORregHEX [blackORwhite_farthestFromHexRGBcolor "$hexRGBcolor"] set textCOLORselHEX [augment_hexRGBcolor "$textCOLORregHEX"] ######################################################### ## Set color for points (interior of ovals/circles). ## ## In the 'update_plot' proc, the outline of the circles ## is drawn with $lineCOLORhex. For some contrast, we ## used a differentiated color, such as $textCOLORselHEX ## --- the 'selected' text color --- or the canvas color, ## which should be quite different from the line color. ######################################################### ## A medium gray. # set pointCOLORregHEX "888888" # set pointCOLORregHEX "$textCOLORselHEX" set pointCOLORregHEX "$hexRGBcolor" ## FOR TESTING: if {0} { puts "PROC 'set_plotColors' :" puts "Canvas color: $hexRGBcolor" puts "Line color: $lineCOLORhex" puts "Text-regular color: $textCOLORregHEX" puts "Text-selected color: $textCOLORselHEX" puts "Point (circle) color: $pointCOLORregHEX" } } ## END OF PROC 'set_plotColors' ##+##################################################################### ## PROC 'blackORwhite_farthestFromHexRGBcolor' ##+##################################################################### ## PURPOSE: For a given hex RGB color, returns either black or white ## --- as the hex RGB string '000000' or 'ffffff'. ## ## Argument:a hex RGB color string, like 'ff9933' ## ## CALLED BY: proc 'set_plotColors' ##+##################################################################### proc blackORwhite_farthestFromHexRGBcolor {hexRGBin} { scan $hexRGBin "%2x%2x%2x" r255 g255 b255 set tempRGBtot [expr {$r255 + $g255 + $b255}] set tempTOTlim 360 # set tempTOTlim 200 if {$tempRGBtot > $tempTOTlim} { set hexRGBout "000000" } else { set hexRGBout "ffffff" } return "$hexRGBout" } ## END OF PROC 'blackORwhite_farthestFromHexRGBcolor' ##+##################################################################### ## PROC 'invert_hexRGBcolor' ##+##################################################################### ## PURPOSE: For a given hex RGB color, invert it. ## Returns the inverted color as a hex RGB string. ## ## Argument:a hex RGB color string, like 'ff9933' ## ## CALLED BY: proc 'set_plotColors' ##+##################################################################### proc invert_hexRGBcolor {hexRGBin} { ## FOR TESTING: # puts "PROC 'invert_hexRGBcolor' :" # puts "hexRGBin: $hexRGBin" scan $hexRGBin "%2x%2x%2x" r255 g255 b255 set R255 [expr {255 - $r255}] set G255 [expr {255 - $g255}] set B255 [expr {255 - $b255}] set hexRGBout [format "%02x%02x%02x" $R255 $G255 $B255] return $hexRGBout } ## END OF PROC 'invert_hexRGBcolor' ##+##################################################################### ## PROC 'augment_hexRGBcolor' ##+##################################################################### ## PURPOSE: For a given hex RGB color, make a color fairly distinct ## from it --- without exceeding the 0-255 limit for the ## RGB channels. ## Returns the 'augmented' color as a hex RGB string. ## ## Argument:a hex RGB color string, like 'ff9933' ## ## CALLED BY: proc 'set_plotColors' ##+##################################################################### proc augment_hexRGBcolor {hexRGBin} { scan $hexRGBin "%2x%2x%2x" r255 g255 b255 ## AUGamt should be less than half of 255, less than 127. # set AUGamt 40 set AUGamt 80 set R255 [expr {$r255 + $AUGamt}] if {$R255 > 255} { set R255 [expr {$r255 - $AUGamt}] } set G255 [expr {$g255 + $AUGamt}] if {$G255 > 255} { set G255 [expr {$g255 - $AUGamt}] } set B255 [expr {$b255 + $AUGamt}] if {$B255 > 255} { set B255 [expr {$b255 - $AUGamt}] } set hexRGBout [format "%02x%02x%02x" $R255 $G255 $B255] return $hexRGBout } ## END OF PROC 'augment_hexRGBcolor' ##+##################################################################### ## PROC 'downsize_canvas' ##+##################################################################### ## PURPOSE: To DOWN-size the canvas. ## ## METHOD: ## We apply a factor to the current width and height of the canvas. ## Then we call 'update_plot' to redo the plot. ## ## Actually we resize the entire GUI window and let the Tk 'pack' ## geometry manager take care of resizing the canvas appropriately ## according to the various '-fill' and '-expand' parameters for ## the widgets of the GUI. ## ## NOTE: ## The user can keep clicking the 'DwnCan' button to downsize ~5% per click. ## ## Arguments: none ## ## CALLED BY: the 'DwnCan' button ##+##################################################################### proc downsize_canvas {} { ############################################################## ## Downsize the GUI (top level) window somewhat. ############################################################## resize_win 0.95 ############################################################# ## Re-do the plot, according to the new canvas size --- which ## should be reduced because entire GUI window was reduced. ## ## The 'update' is necessary because 'update_plot' does not ## do 'update' before querying for the current canvas size. ############################################################# update update_plot } ## END OF PROC 'downsize_canvas' ##+##################################################################### ## PROC 'upsize_canvas' ##+##################################################################### ## PURPOSE: To UP-size the canvas. ## ## METHOD: ## We apply a factor to the current width and height of the canvas. ## Then we call 'update_plot' to redo the plot. ## ## Actually we resize the entire GUI window and let the Tk 'pack' ## geometry manager take care of resizing the canvas appropriately ## according to the various '-fill' and '-expand' parameters for ## the widgets of the GUI. ## ## NOTE: ## The user can keep clicking the 'UpCan' button to upsize ~5% per click. ## ## Arguments: none ## ## CALLED BY: the 'UpCan' button ##+##################################################################### proc upsize_canvas {} { ################################################################ ## Upsize the GUI (top level) window somewhat. ################################################################ resize_win 1.05 ########################################################### ## Re-do the plot, according to the new canvas size --- ## which should be enlarged because window was enlarged. ## ## The 'update' is necessary because 'update_plot' does not ## do 'update' before querying for the current canvas size. ########################################################### update update_plot } ## END OF PROC 'upsize_canvas' ##+##################################################################### ## PROC 'resize_win' ##+##################################################################### ## PURPOSE: To UP/DOWN-size the Tk window by a given factor. ## ## METHOD: Several methods could be used. ## For now, we query the top level (GUI) window width and height ## (and location) with 'wm' (rather than 'winfo') and apply the ## input argument ('factor') to the current window width and height. ## ## Then reset the window size (and location) with 'wm'. ## ## Arguments: one --- 'factor' ## ## CALLED BY: 'upsize_canvas' and 'downsize_canvas' procs ##+##################################################################### proc resize_win {factor} { ## This is not the exact window DIMENSIONS we want. ## These do not include the window decoration. # set winXlenPx [ winfo width . ] # set winYlenPx [ winfo height . ] ## FOR TESTING: # puts "FROM proc 'resize_win':" # puts "winfo-width-winXlenPx : $winXlenPx" # puts "winfo-width-winYlenPx : $winYlenPx" ## This is not the window LOCATION we want. ## It is the upper-left of the window without the window decoration. # set winXlocPx [ winfo rootx . ] # set winYlocPx [ winfo rooty . ] ## FOR TESTING: # puts "FROM proc 'resize_win':" # puts "winfo-rootx-winXlocPx : $winXlocPx" # puts "winfo-rooty-winYlocPx : $winYlocPx" ## NOTE: ## [wm geometry .] returns integers of the form ## ${winXlenPx}x${winYlenPx}+${winXlocPx}+${winYlocPx} set WMgeom [wm geometry .] set winXYlenPx [lindex [split $WMgeom '+'] 0] set winXlenPx [lindex [split $winXYlenPx 'x'] 0] set winYlenPx [lindex [split $winXYlenPx 'x'] 1] set winXlocPx [lindex [split $WMgeom '+'] 1] set winYlocPx [lindex [split $WMgeom '+'] 2] ## FOR TESTING: # puts "FROM proc 'resize_win':" # puts "wm-winXlenPx : $winXlenPx" # puts "wm-winYlenPx : $winYlenPx" # puts "wm-winXlocPx : $winXlocPx" # puts "wm-winYlocPx : $winYlocPx" ## Reduce the window size according to argument 'factor'. set winXlenPx [expr {int(floor ( $factor * $winXlenPx ))} ] set winYlenPx [expr {int(floor ( $factor * $winYlenPx ))} ] ## Adjust the 'locPx' vars for the window manager border. ## EVEN 'wm' does not account for the window decoration!!! ## We will have to adjust, to (try to) keep the upper-left corner ## of the window in place. set winXlocPx [ expr {$winXlocPx - 3} ] set winYlocPx [ expr {$winYlocPx - 23} ] wm geometry . ${winXlenPx}x${winYlenPx}+${winXlocPx}+${winYlocPx} } ## END OF PROC 'resize_win' ##+##################################################################### ## PROC 'getPut_image' ##+##################################################################### ## PURPOSE: To put one or more images (e.g. a logo) on the canvas. ## ## METHOD: ## Uses 'tk_getOpenFile' dialog to get an image filename (GIF or PNG). ## Uses Tk 'image create photo' command to create an in-memory image structure. ## Uses canvas 'create image' command to put the image on the canvas. ## ## NOTE: (on a Tk postscript limitation) ## A Tk image on a Tk canvas is not captured by the Tk canvas 'postscript' ## command. But a screen image could be captured --- in a PNG or GIF or JPEG ## file --- with a screen capture utility such as 'gnome-screenshot' --- ## for use in e-mails, web pages, or other docs. ## The screen image, in a PNG file say, could be printed with the Print ## option in a utility such as a web browser. ## ## Arguments: none ## ## CALLED BY: the 'GetImg' button ##+##################################################################### proc getPut_image {} { ## FOR TESTING (de-activating this proc): # return ## DIRimages is set in the 'Additional GUI Initialization' section ## at the bottom of this script. global CURcanWidthPx CURcanHeightPx DIRimages set fName "" set fName [tk_getOpenFile -parent . \ -title "Select an image file (GIF or PNG)." \ -initialdir "$DIRimages" ] ## FOR TESTING: # puts "fName : $fName" if {"$fName" == ""} {return} if {[file exists "$fName"]} { ################################################ ## If the filename from the file selector exits, ## 1) create the in-memory image ## 2) put the in-memory image on the canvas ## --- near bottom-right of canvas. ############################################### set imgID [image create photo -file "$fName"] set xPx [expr { int(0.95 * $CURcanWidthPx) } ] set yPx [expr { int(0.95 * $CURcanHeightPx) }] .fRlow.fRplot.can create image \ $xPx $yPx -anchor se -image $imgID \ -tags {TAGmoveable TAGimage} ## Could pop a message here to user letting them know ## that the image is draggable --- and that Tk ## Postscript output from a Tk canvas will not show ## the image --- and they can 'delete' the image ## by dragging it off the canvas. Alternative: ## .fRlow.fRplot.can delete TAGimage } ## END OF if {[file exists "$fName"]} } ## END OF PROC 'getPut_image' ##+##################################################################### ## PROC 'print_preview' ##+##################################################################### ## PURPOSE: To make and view a Postscript file of the canvas image. ## ## Arguments: none ## ## CALLED BY: the 'PrtPreview' button ##+##################################################################### proc print_preview {} { global env PSviewer DIRtemp #################################################### ## Make a name of the to-be-created postscript file. #################################################### set tmp_filename "${DIRtemp}/$env(USER)_tmp_tkplot.ps" ##################################################### ## Remove a file by the same name from a previous run. ##################################################### ## It looks like we can do it without this 'eval' method. # eval exec rm -f "$tmp_filename" exec rm -f "$tmp_filename" ############################### ## Make the postscript file. ############################### .fRlow.fRplot.can postscript -file "$tmp_filename" \ -colormode color \ -pageheight 10i \ -pagewidth 7i \ -pagex 1i \ -pagey 1i \ -pageanchor sw # -colormode color # -colormode gray # -colormode monochrome ################################################ ## Show the postscript file in a viewer program. ################################################ ## It looks like we can do it without this 'eval' method. # eval exec "$PSviewer \"$tmp_filename\" &" exec $PSviewer "$tmp_filename" & ###################################################### ## We could delete the file. But leave it for the user ## in case they want to do something else with it. ###################################################### # exec rm -f "$tmp_filename" } ## END OF PROC 'print_preview' ##+##################################################################### ## PROC 'print_plot' ##+##################################################################### ## PURPOSE: To make and send a Postscript file of the canvas image ## to a printer. ## ## Arguments: none ## ## CALLED BY: the 'Print' button ##+##################################################################### proc print_plot {} { global env PRINTcmd DIRtemp #################################################### ## Make a name of the to-be-created postscript file. #################################################### set tmp_filename "${DIRtemp}/$env(USER)_tmp_tkplot.ps" ##################################################### ## Remove a file by the same name from a previous run. ##################################################### eval exec rm -f "$tmp_filename" ################################################### ## Make the postscript file. ################################################### .fRlow.fRplot.can postscript -file "$tmp_filename" \ -colormode color \ -pageheight 10i \ -pagewidth 7i \ -pagex 1i \ -pagey 1i \ -pageanchor sw # -colormode color # -colormode gray # -colormode monochrome ################################################ ## Print the postscript file using a command ## set in the 'ADDITIONAL GUI INITIALIZATION' ## section at the bottom of this script. ################################################ eval exec $PRINTcmd "$tmp_filename" ###################################################### ## We could delete the file. But leave it for the user ## in case they want to do something else with it. ###################################################### # eval exec rm "$tmp_filename" } ## END OF PROC 'print_plot' ##+######################################################################## ## PROC: 'popup_shortmsg' ##+######################################################################## ## PURPOSE: Popup a short message to user --- usually a one or two ## line error message. ## ## METHOD: We can use 'tk_dialog' or we can use the ## 'popup_msgVarWithScroll' proc below. ## ## CALLED BY: various procs --- 'update_plot' and maybe more. ##+####################################################################### proc popup_shortmsg { title message } { tk_dialog .xxx "$title" "$message" warning 0 Close # popup_msgVarWithScroll .xxx "$message" +40+40 } ## END OF PROC 'popup_shortmsg' ##+######################################################################## ## 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 button. ## ## 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 Doyle,'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 these 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_SMALL_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" ## -font fontTEMP_SMALL_varwidth scrollbar $toplevName.scrolly \ -orient vertical \ -command "$toplevName.text yview" scrollbar $toplevName.scrollx \ -orient horizontal \ -command "$toplevName.text xview" } else { text $toplevName.text \ -wrap none \ -font fontTEMP_SMALL_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 ## -font fontTEMP_SMALL_varwidth } button $toplevName.butt \ -text "OK" \ -font fontTEMP_varwidth \ -command "destroy $toplevName" ############################################### ## PACK *ALL* the widgets in frame '$toplevName'. ############################################### ## Pack the bottom button BEFORE the ## bottom x-scrollbar widget, pack $toplevName.butt \ -side bottom \ -anchor center \ -fill none \ -expand 0 if {$VARheight > 10} { ## Pack the scrollbars BEFORE the text widget, ## so that the text does not monopolize the space. pack $toplevName.scrolly \ -side right \ -anchor center \ -fill y \ -expand 0 ## DO NOT USE '-expand 1' HERE on the Y-scrollbar. ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA. pack $toplevName.scrollx \ -side bottom \ -anchor center \ -fill x \ -expand 0 ## DO NOT USE '-expand 1' HERE on the X-scrollbar. ## THAT KEEPS THE TEXT AREA FROM EXPANDING. pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } else { pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } ################################################ ## Set some 'event' bindings to allow for ## easy scrolling through huge listings. ## is a press of the Page-Down key. ## is a press of the Page-Up key. ## is a press of the Home key ## to go to the top of the listing. ## is a press of the End key ## to go to the bottom of the listing. ## is a press of the Up-arrow key. ## is a press of the Down-arrow key. ################################################ bind $toplevName "$toplevName.text yview scroll +1 page" bind $toplevName "$toplevName.text yview scroll -1 page" bind $toplevName "$toplevName.text see 1.0" bind $toplevName "$toplevName.text see end" bind $toplevName "$toplevName.text yview scroll -1 unit" bind $toplevName "$toplevName.text yview scroll +1 unit" ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## $toplevName.text delete 1.0 end $toplevName.text insert end $VARtext $toplevName.text configure -state disabled } ## END OF PROC 'popup_msgVarWithScroll' ##+############################################### ## END OF PROCS SECTION. ##+############################################### ## SET HELP TEXT variable. ##+############################################### set HELPtext \ "**** tkPlotQuik - Line and/or Point Plots from a File of Data Columns **** This Tk GUI script provides an intuitive, easy-to-use XY-AXIS FILE-PLOT UTILITY -- that reads data from a text file, allowing the user to specify 2 or 3 columns of data to plot, from among columns of data in the file. This utility is meant to allow the user to make plots QUICKLY --- plots of sufficient quality to get the user's ideas 'across'. This 'file-plot' utility is oriented toward making plots of many data points --- tens or hundreds --- even thousands of data points --- via connecting lines --- or, optionally, using data point markers. The GUI provides a couple of checkbutton widgets to allow for choosing 'DataLines' and/or 'DataPoints'. ******* THE GUI: This Tk script presents a GUI with a 'canvas' widget on which to show a 2-D line (or point) plot --- with plot-title and axis-titles and tic-mark labels that can be dragged with the mouse. This script presents a GUI to prompt for a file name --- and to prompt for which column of data to use for the x-axis and which column of data to use for y1 data --- with an option to specify a 3rd column for y2 data. The y2 data option is to allow for comparing data such as econometric data --- such as data at months 1 through 12 of two different years. Besides entry fields for the data filename and for column numbers, this script provides entry fields in the GUI to prompt for - x,y axis min-max limits - x,y axis tic-mark distances - plot title - x,y axis titles. This script 'suggests' axis min-max limits and axis tic-mark distances, based on the data read from the specified columns. But the user can over-ride those suggestions, which are put in the entry fields of the GUI. *************** OPERATING GUIDE: When the GUI first comes up, its entry fields are populated with some sample - column numbers - plot-title and axis titles - x,y axis limits - x,y tic-mark distances. The title names, when plotted on the canvas, indicate WHERE the 3 types of titles are initially put on the plot canvas. The user can 'drag' these 3 titles around to different places on the 'canvas' with a mouse. Just click on a title, hold down the button (button-1), and drag to a new position. Then release the button. In fact, you can 'whip' titles out-of-sight --- off an edge of the 'canvas'. If you want the titles back, you can simply click on the 'UpdatePlot' button. AND ... you can 'drag' the tic-mark labels. If you decide there are too many, you can 'whip' them off the canvas. You are not given the option to drag the data points, because you could end up with a plot that is not in 'sync' with the data in the input file. BUT you can edit the input file to change some x or y data values, click the '(Re)ReadFile' button, and then click on the 'UpdatePlot' button. The GUI could be initialized with different data and titles. At the bottom of the script --- in the 'Additional GUI Initialization' section --- there are some other sample data values that could be activated instead of this set of data. --- There are a good number of plot options. Here are some. *********************** CANVAS BACKGROUND COLOR: You can click on the 'CanvasColor' button and use an RGB color selector GUI that pops up to change the canvas background color. If you are going to capture the plot in an image file (such as PNG or GIF) and then print the image (say by reading the image file into a web browser and using the print capability of the web browser), you will probably want to change the plot background to WHITE --- to save on ink. Rather than make a crowded GUI more crowded by supplying buttons to set colors of text (titles and tic-mark labels) and colors of lines (axes, tic-marks, data lines, and point outlines), the text and line colors are set 'automatically' based on the canvas color that is chosen. In particular, if a dark canvas color is chosen, the text and lines will be shown in a light color --- and if a light canvas color is chosen, the text and lines will be shown in a dark color. ******************************** THE 'DwnCan' and 'UpCan' BUTTONS: The 'canvas' area expands and contracts if you expand or contract the entire GUI window --- and click on the 'UpdatePlot' button. A faster way to quickly downsize or upsize the window is to click on the 'DwnCan' or 'UpCan' button. This can be quicker than trying to get the attention of an extremely thin 'sensitive edge' of the GUI to drag the window to a different size. One thing to be aware of: The 'DwnCan' button will probably not work if the user has maximized the GUI window via the window manager 'Maximize' option or by repeated use of the 'UpCan' button. To make the 'DwnCan' button useable again, you can simply use the 'UnMaximize' ('Restore') option of the window manager, on the title bar of the GUI window. *********** PLOT BORDER: You can click on the 'PlotBorder' checkbutton to turn it ON, and then click on the 'UpdatePlot' button to cause a border (a line) to be drawn around the plot. Turn the 'PlotBorder' checkbutton OFF and click 'UpdatePlot' to revert to a plot without the border. ********************* THE 'GetImage' BUTTON: The 'GetImage' button at the top of the GUI allows the user to get an image (like a logo) to put on the plot canvas. A file-selector GUI pops up and allows the user to select an image file (GIF or PNG) to put on the canvas. When a user selects a filename and clicks on the 'Open' button of the file-selector, the file-selector dialog window disappears and the image appears at the lower-right of the 'canvas' area. Like for titles and tick-mark labels, the user can 'drag' the image from its initial placement on the canvas to anywhere else on the canvas --- and the image can be 'whipped' off of the canvas. More than one image can be fetched and placed on the canvas. ********************************** POSTSCRIPT PREVIEW & PRINT OPTIONS: The 'PrtPreview' and the 'Print' buttons create a Postscript file in a '/tmp' directory. The 'PrtPreview' button uses a PDF/Postscript viewer utility --- such as 'evince' --- to show the Postscript file that is created. And the 'Print' button uses a print-command --- shown in an 'entry' field at the top-right of the GUI --- to send the Postscript code to a user's printer. The temporary-files-directory, the PDF/Postscript viewer-command, and the print-command can be changed by the user who downloads this script by changing 3 variables in 'set' statements at the bottom of the script in the 'Additional GUI Initialization' section: - DIRtemp - PSviewer - PRINTcmd For the print command, I found that the program '/usr/bin/cupsdoprint' of the CUPS printing system worked quite nicely --- on my Ubuntu 9.10 Linux system --- with the set of parameters seen in this code. NOTE: The Tk canvas 'postscript' option does not capture an image placed on the canvas. It captures points, curves, and text placed on the canvas. If you want to capture an image of the plot including any GIF or PNG images put on the canvas, you can use an image capture utility (such as 'gnome-screenshot') to capture the image in a PNG or GIF or JPEG file. You can crop the image with an interactive image editor (such as 'mtpaint') and then print the image file with an image-view/print utility (such as 'eog' = 'eye of gnome') or a web browser, as described above --- in the Canvas Background Color section. NOTE: The Postscript options are not What-You-See-Is-What-You-Get (WYSIWYG). You may find that you get a better quality plot using a screen capture. ***************** THE 'Help' BUTTON: The 'Help' button on the GUI can provide more extensive help than the brief guide on the left side of the GUI. In fact, it presents this help. ***************************** TIC-MARK-LABEL DECIMAL PLACES: You can control the number of decimal places in the tic-mark-labels, by the number of decimal places in the 'XticDist' and 'YticDist' entry fields. For example, an 'XticDist' entry of 100.0 will cause the tic-mark-labels to end with '.0' --- BUT if the 'XticDist' entry is 100, the tic-mark-labels will be integers without a decimal point on the end. ********** CONCLUSION: This is a plotting-utility implementation using BASIC Tcl-Tk commands, i.e. not requiring an 'extension' of Tcl or Tk. Unfortunately, there do not seem to be any FAIRLY GENERAL, yet RELATIVELY SIMPLE, data-file plotting scripts at Tcl-Tk code archive sites --- even in 2013, more than 20 years after the development of the necessary Tk canvas facilities to support plotting. Nor are such general, easy-to-use data-file plotting Tk scripts available via web searches on keyword strings such as 'bin wish canvas' or 'wish button label entry create line'. Even searches on 'canvas' and 'create line' on the wiki.tcl.tk site, in early 2013, yield only simplistic line plot 'demos' that are not suited to general and FAST line-and/or-point plots --- from text files of columnar data. This script is meant to fill that long-time void. " ##+###################################################### ##+###################################################### ## ADDITIONAL GUI INITIALIZATION section: ##+###################################################### ## - Set line-width value (for axes, tic-marks, bar outlines) ## --- which is not currently settable via widgets on the GUI. ## (GUI too 'busy' and too many choices already) ## ## - Set initial canvas background color and from that canvas ## color set line color (for axes, tic-mark labels, bar outlines) ## and set text color (for titles and tic-mark labels). ## NOTE: ## Line and text colors are not currently settable via widgets ## on the GUI. (GUI too 'busy' and too many choices already) ## So we set those colors 'automatically' from the canvas color. ## ## - Set some directories and helper-apps. ## ## - Set some initial 'entry' field values for a plot and ## make a plot from those entries. ##+###################################################### ##+####################################################################### ## Set line width (in pixels) --- for drawing lines such as ## axis lines and tic-mark lines and rectangles on the canvas. ## ## NOTE: ## Instead of hard-coding a value, we could set the width based on ## the screenwidth --- like 0.001 or 0.002 of screenwidth in pixels. ##+####################################################################### # set lineWIDTHpx 2 set SCRNsizexPx [winfo screenwidth .] # set lineWIDTHfactor 0.002 set lineWIDTHfactor 0.001 set lineWIDTHpx [expr {int($lineWIDTHfactor * $SCRNsizexPx) + 1}] ##+#################################################### ## Set an initial BACKGROUND COLOR FOR THE CANVAS --- ## and use that to set some 'related' (global) color variables: ## lineCOLORhex ## textCOLORregHEX ## textCOLORselHEX ## 'reg' = regular ; 'sel' = selected ## ## We set the canvas color a little lighter (brighter) ## than the GUI 'palette' color (set near the top of ## this code) --- to make the canvas stand out. ##+#################################################### set CANr255 230 set CANg255 230 set CANb255 230 set CANrgbHEX [format "%02X%02X%02X" $CANr255 $CANg255 $CANb255] set_plotColors "$CANrgbHEX" update_cancolor_button # .fRlow.fRplot.can configure -bg "#$CANrgbHEX" ##+#################################################### ## Set the full-name of the RGB color-selector Tk script ## that is used in one or more 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]" } ## It the color selector script is in the directory with this Tk script: # set RGBcolorSelectorScript "$DIRthisScript/sho_colorvals_via_sliders3rgb.tk" ## Alternatively: If the RGB color-selector Tk script is in the ## SELECTORtools directory of the FE 'tkGooies' menu/toochest system, ## use something like the following. set DIRupOne "[file dirname "$DIRthisScript"]" set DIRupTwo "[file dirname "$DIRupOne"]" set RGBcolorSelectorScript "$DIRupTwo/SELECTORtools/tkRGBselector/sho_colorvals_via_sliders3rgb.tk" ##+#################################################### ## Set a directory from which to get images. ## (We could put a few images in the directory with this ## Tk script --- or a lot of images in an 'images' ## subdirectory of the directory of this Tk script. ## Or use some subdirectory of the user's home directory.) ##+#################################################### # set DIRimages "$env(HOME)/Photos" # set DIRimages "${DIRthisScript}/images_GIF" # set DIRimages "${DIRthisScript}/images_PNG" set DIRimages "$DIRthisScript" ##+#################################################### ## Set a files directory for the Postscript print ## options --- 'PrtPreview' and 'Print' --- ## for a 'temporary' Postcript file. ##+#################################################### set DIRtemp "/tmp" ##+#################################################### ## Set a Postscript viewer program for use in the ## 'print_preview' proc. ##+#################################################### # set PSviewer "/usr/bin/xpdf" # set PSviewer "evince" set PSviewer "/usr/bin/evince" ##+#################################################### ## Set a print-command for use in the 'print_plot' proc. ## Show the print-command in an 'entry' widget at the ## top of the GUI. ##+#################################################### # set PRINTcmd "/usr/bin/kprinter" # set PRINTcmd "/usr/bin/hp-print" set PRINTcmd "/usr/bin/cupsdoprint -P EPSON-Epson-Stylus-NX430 -H localhost:631" set ENTRYprintcmd "$PRINTcmd" ##+###################################################### ## Set an inital directory from which to start navigating ## to choose a data file for plotting. ##+###################################################### ## FOR TESTING: # set DIRdatafiles [pwd] # set DIRdatafiles "$env(HOME)" set DIRdatafiles "$DIRthisScript" ##+###################################################### ## 6 IS THE DEFAULT VALUE OF $tcl_precision. ## Should we set it higher? Probably not necessary. ##+###################################################### # set tcl_precision 6 # set tcl_precision 16 ##+###################################################### ## We can activate one of the following sets of initial ## GUI/plot var settings by changing 'if {0}' to 'if {1}'. ##+#################################################### ## NOTE: ## The user can add leading spaces to the titles to help ## position them horizontally. In addition, the titles ## can be dragged. ##+###################################################### ## A generic/default plot example. No plot filename provided. if {1} { set ENTRYfilename "" set ENTRYxcolNUM "1" set ENTRYy1colNUM "2" set ENTRYy2colNUM "" set ENTRYtitleMain " MAIN PLOT TITLE goes here." set ENTRYtitleXaxis " X-axis title goes here." set ENTRYtitleY1axis " Y-axis title goes here. " set ENTRYxaxisMIN "0" set ENTRYxaxisMAX "720" set ENTRYxaxisTICDIST "20" set ENTRYy1axisMIN "-100" set ENTRYy1axisMAX "100" set ENTRYy1axisTICDIST "20" } ## END OF if {0} and generic/default initial GUI entry settings. ## A world population plot example --- with plot filename data being plotted ## as the GUI starts up. if {0} { set ENTRYfilename "$DIRthisScript/DATA2cols_worldpopulation_yearVSmillions.txt" set ENTRYxcolNUM "1" set ENTRYy1colNUM "2" set ENTRYy2colNUM "" set ENTRYtitleMain " World Population Growth -- year 0 to year 2000" set ENTRYtitleXaxis " Year A.D." set ENTRYtitleY1axis " World Population in millions" set ENTRYxaxisMIN "0" set ENTRYxaxisMAX "2050" set ENTRYxaxisTICDIST "50" set ENTRYy1axisMIN "0" set ENTRYy1axisMAX "8000" set ENTRYy1axisTICDIST "1000" ####################################################### ## Instead of waiting until the user specifies ## a data filename, and clicks the '(Re)ReadFile' ## button to read the data, and clicks the 'UpdatePlot' ## button to do a plot according to the entries ## that are in the GUI --- for columns, min-max limits, ## titles, axis labels: ## ## We could liven this GUI up by loading the ## ENTRYfilename var with an existing filename --- ## and then use the 'read_data' and 'update_plot' ## procs to startup with a plot of the file that ## is named in the ENTRYfilename var --- for example, ## a world-population growth file, like above. ## ## (Since 'update_plot' does not do an 'update' before ## querying the current canvas size, we need to do it here ## --- to force the 'wish' interpreter to do all the ## widget packing with the 'pack' geometry manager, ## before 'update_plot' does its thing.) ##+###################################################### read_data update update_plot } ## END OF if {0} and world-population example.