#!/usr/bin/wish -f ##+######################################################################## ## ## SCRIPT: meters_memory-swap_hidef-image.tk ## ## ## PURPOSE: This script is meant to show a GUI that holds a pair of ## tachometer-style meters --- rendered using a nice-looking, ## high-definition-image for the background of each meter. ## ## The 'needles' ('pointers') on the 2 meters can be updated ## periodically (automatically and/or manually) to show the ## amount of memory and swap space in use. ## ## This Tk script was developed on Linux and uses the 'free' ## command to periodically get the memory and swap data to ## determine where to relocate the position of the 2 pointers. ## ################################### ## METHOD OF DRAWING THE TWO METERS: ## ## A previously published version of this utility, by this author, ## used Tk canvas 'create arc' and 'create line' commands to draw a ## tachometer-style meter --- using a 'shadow-circle' technique of ## Marco Maggi (http://wiki.tcl.tk/9107) to make nice-looking ## meter components. ## ## 'create line' was used to draw tic marks around the meter arc. ## Furthermore, 'create line' was used to draw a needle on the meter, ## at various angles. ## ## THIS Tk script uses the Tk canvas 'create image' command to put a ## nice-looking, HD (high-definition) meter background on the canvas ## of each of the two meters (memory and swap). ## ## Then 'create line' may (optionally) be used to put tic marks, ## in an arc, on the meter background --- if there are not tic marks ## on the background image already. ## ## To avoid problems that may come in trying to draw an HD (hi-def) needle ## at various angles on the meter, we use a 'bullet' image (a small 'blunt-pointer' ## image), placed along the tic-marks arc, to indicate the current value of ## memory or swap used. We generally use a color for this 'bullet' ## which contrasts well with the background image for the meter. ## ## By means of Tcl arrays, we allow the user to choose from multiple ## 'themes' for the meter. (The user can choose a theme via a ## 'scale' widget rather than 'radiobuttons' --- to allow for ## supporting an almost unlimited number of themes, rather than being ## limited by the amount of space that radiobuttons consume on the GUI.) ## ## Each theme (indexed via an integer) includes the following items, ## stored in Tcl arrays: ## - a BACKGROUND IMAGE (a GIF or PNG file) - and we allow for a ## small, medium, and large background images (about 200, 350, ## and 500 pixels high) ## - a BULLET/POINTER IMAGE (a small transparent GIF or PNG file) ## - a RIVET IMAGE (a small transparent GIF or PNG file), which ## is shown optionally, according to a checkbutton on the GUI ## - some parameters, such as ## - the CENTER LOCATION on the background image (in case it is ## not symmetric) --- typically about 0.5,0.5 ## - a RADIUS for an arc, for positioning tic marks and tic mark ## labels --- as well as for positioning the 'bullet' image. ## (Typically this radius value will be about 0.25.) ## - an indicator of whether the tic marks & labels should be ## PLACED ON THE INSIDE OR THE OUTSIDE OF THE ARC. (This ## indicator is set to 'in' or 'out'.) ## - an indicator of WHETHER TIC MARKS ARE TO BE DRAWN/PLACED. ## Tic marks are not needed if they are already on the ## background image. ## - START and EXTENT angles for the arc, such as -70 and 320, ## where 320 = 70 + 180 + 70. ## (The arc is drawn counter-clockwise, but we use it in a ## clockwise fashion.) ## ## NOTE1: ## The center location and radius parameters are to be given as ## ratios (not pixels) --- so that they work for any size of the ## background image --- small, medium, or large. ## NOTE2: ## We do not require the meter background images to be square. ## They can be rectangular. ## NOTE3: ## The 'theme arrays' are set in one section of this script. To change ## the themes, simply edit the 'set' statements for those arrays. ## To add themes, add similar array-setting statements and change the ## range of the theme-scale. ## ################### ## THE GUI LAYOUT: ## ## The options available to the user are indicated ## by the following 'sketch' of the GUI. ## ## In the GUI 'sketch' 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-O indicates a Tk 'radiobutton' widget. ## - CAPITAL-X indicates a Tk 'checkbutton' widget. ## - A LINE OF HYPHENS with arrow-heads on each end (<-------->) ## indicates a 'scale' widget. ## ## --------------------------------------------------------------- ## PERCENT of Total MEMORY and SWAP in USE [window title] ## --------------------------------------------------------------- ## {Exit} {Help} {Refresh} O Small O Medium O Large ## ## Theme for ## the meter : <--------------------> X DrawTicLabels ## ## X DrawTicsArc X ShowCornerFeatures (if in theme) ## ## SampleRate Samples: NNN ## (seconds) : <----------------------------> ## ## ## [The two meters will be placed in two side-by-side frames ## in the area below. Each frame will contain two label lines ## above a canvas widget. A meter background image will be placed ## on the two canvas widgets.] ## ## [Tic marks can be drawn in an arc on the meter background, ## with 'create line', if they are not already on the ## background image.] ## ## [The check-buttons, above, can be used by the user ## to specify whether the labels (0 to 100) should be ## drawn/shown (with 'create text') --- and whether an ## arc should be drawn/shown (with 'create arc') along the ## path of the tic marks.] ## ## [Optionally, as part of the 'theme' selected, 4 images ## ('rivets' or screw-heads or whatever) can be placed ## in the 4 corners of the background image.] ## ##+################ ## NOTES ON THE GUI: ## ## 1) There is a SCALE widget for the user to set a ## 'wait-seconds' parameter for auto-refresh of the meters --- ## in seconds --- down to tenths of seconds, and up to multiple ## minutes. ## ## 2) There is an 'fRcanvases' frame to contain 2 CANVAS widgets that ## hold the two meter images, in 2 canvases, side by side. ## Also the 'fRcanvases' frame holds some LABEL widgets, to show ## TOTAL-MEMORY and USED-MEMORY and TOTAL-SWAP and USED-SWAP, ## as text items. ## ## 3) A 'Refresh' button on the GUI can be used to reset ## the start time for sampling --- at the current rate ## (a sampling interval, set via the scale widget). ## ## The 'Refresh' button is useful if the sampling rate is set ## too high or too low and the user wants to immediately ## reset the sampling rate. ## ##+############## ## GUI components: ## ## From the GUI 'sketch' and notes above, it is seen that the GUI consists of ## about ## ## - 3 button widgets ## - 7 label widgets ## - 3 radiobutton widgets in 1 group ## - 3 checkbutton widgets ## - 2 scale widgets ## - 2 canvas widgets ## - 0 entry widgets ## - 0 listbox widgets ## ##+#################################### ## METHOD USED to update the two meters: ## ## A Tcl 'exec' command calls on a separate shell script that uses ## the 'free' command to get the memory and swap data and ## extract-and-format the data for return to this Tk script. ## ## (Since extraction of the data from the 'free' command is rather ## simple, the use of a separate 'external' script could be ## avoided by including some of the formatting code within ## the command string called by 'exec'.) ## ##+####################### ## CAPTURING THE GUI IMAGE: ## ## A screen/window capture utility (like 'gnome-screenshot' ## on Linux) can be used to capture the GUI image in a PNG ## or GIF file, say. ## ## If necessary, an image editor (like 'mtpaint' on Linux) ## can be used to crop the window capture image. The image ## could also be down-sized --- say to make a smaller image ## suitable for use in a web page or an email. ## ##+####################################################################### ## 'CANONICAL' STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name, win-position, win-color-scheme, ## fonts, widget-geom-parms, win-size-control, text-array-for-labels-etc). ## ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack the frames. ## ## 2) Define & pack all widgets in the frames, frame by frame. ## After all the widgets for a frame are defined, pack them in the frame. ## ## 3) Define keyboard or mouse/touchpad/touch-sensitive-screen 'event' ## BINDINGS, if needed. ## ## 4) Define PROCS, if needed. ## ## 5) Additional GUI INITIALIZATION (typically with one or more of ## the procs), if needed. ## ##+################################# ## Some detail of the code structure of this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : ## '.fRbuttons' - to contain 'Exit', 'Help', 'Refresh' buttons, ## and S/M/L radiobuttons. ## '.fRtheme1' - to contain a scale widget and a checkbutton widget ## '.fRtheme2' - to contain checkbutton widgets ## '.fRsamprate' - to contain a scale widget, with a label widget. ## '.fRcanvases' - to contain 2 canvas widgets, which will display ## the two meters, side-by-side. ## ## Sub-frames: ## '.fRcanvases.fRcanvas1' - for 2 label widgets & 1 canvas widget ## '.fRcanvases.fRcanvas2' - for 2 label widgets & 1 canvas widget ## ## 1b) Pack ALL frames. ## ## 2) Define & pack all widgets in the frames -- basically going through ## frames & their interiors in left-to-right, or top-to-bottom order. ## ## 3) Define BINDINGS: perhaps one or two ## (See the BINDINGS section of the code for details.) ## ## 4) Define PROCS: ## ## 'make_meters' - to draw 2 meters within their 2 Tk canvases. ## ## The size of the canvas will be determined ## by the size of the background image, which in ## turn is determined by the theme-index and ## the setting of the Small-Medium-Large radiobuttons. ## ## 'make_one_meter' - called by 'make_meters', to make each meter. ## ## 'update_pointers' - to update the pointers on the 2 meters. ## ## 'update_one_pointer' - called by 'update_pointers', to draw the ## pointer (bullet, needle, whatever) on a meter. ## ## 'Refresh' - called by 'Refresh' button. Runs 'make_meters' ## and 'update_pointers'. ## ## 'draw_rivet' - called by 'make_one_meter', 4 times, to put ## rivets in 4 corners around a meter. ## ## 'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var. ## ## ## 5) Additional GUI Initialization: ## - call 'make_meters' to put the 2 meters on the 2 canvases ## - call 'update_pointers' --- to initialize the pointer locations ## and start the updating of the GUI ## according to an initial setting of ## the WAITseconds scale variable. ## ##+####################################################################### ## 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 2015oct30 Started the basic code of the ## script based on the tachometer ## demo script by Marco Maggi ## at http://wiki.tcl.tk/9107. ## Also used the code of the 'hidef' ## script for CPU activity. ## Changed by: Blaise Montandon 2015oct31 Changed some 'pointer' images. ## Changed by: Blaise Montandon 2015nov09 Finalized set of 10 themes and ## their positioning parameters. #+######################################################################## ##+###################################################### ## Set WINDOW TITLE and POSITION. ##+###################################################### wm title . "% of Total MEMORY & SWAP in Use" wm iconname . "MemSwap" wm geometry . +15+30 ##+###################################################### ## Set the COLOR SCHEME for the window and its widgets --- ## such as radiobutton and checkbutton and scale ## 'trough' background color. ##+###################################################### tk_setPalette "#e0e0e0" set scaleBKGD "#f0f0f0" set radbuttBKGD "#f0f0f0" set chkbuttBKGD "#f0f0f0" # set entryBKGD "#ffffff" # set listboxBKGD "#ffffff" ##+######################################################## ## DEFINE (temporary) FONT NAMES. ## ## We use a VARIABLE-WIDTH font for text on LABEL and ## BUTTON widgets. ## ## We use a FIXED-WIDTH font for LISTBOX lists, ## for Help-text in a TEXT widget, and for ## the text in ENTRY fields, if any. ##+######################################################## font create fontTEMP_varwidth \ -family {comic sans ms} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_varwidth \ -family {comic sans ms} \ -size -12 \ -weight bold \ -slant roman ## Some other possible (similar) variable width fonts: ## Arial ## Bitstream Vera Sans ## DejaVu Sans ## Droid Sans ## FreeSans ## Liberation Sans ## Nimbus Sans L ## Trebuchet MS ## Verdana font create fontTEMP_fixedwidth \ -family {liberation mono} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_fixedwidth \ -family {liberation mono} \ -size -12 \ -weight bold \ -slant roman ## Some other possible fixed width fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## DejaVu Sans Mono ## Droid Sans Mono ## FreeMono ## Nimbus Mono L ## TlwgMono ##+########################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. width and height of canvas, and padding for Buttons) ##+########################################################### ## BUTTON widget geom settings: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## LABEL widget geom settings: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 ## RADIOBUTTON widget geom settings: set PADXpx_radbutton 0 set PADYpx_radbutton 0 set BDwidthPx_radbutt 2 ## CHECKBUTTON widget geom settings: set PADXpx_chkbutton 0 set PADYpx_chkbutton 0 set BDwidthPx_chkbutt 2 ## SCALE widget geom parameters: set BDwidthPx_scale 2 set initScaleLengthPx 200 set scaleThicknessPx 10 ## CANVAS widget geom settings: set initCanWidthPx 200 set initCanHeightPx 200 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 ##+###################################################################### ## Set a MIN-SIZE of the window (roughly). ## ## For WIDTH, allow for the min-width of the '.fRbuttons' and '.fRcanvas' ## frames --- at least, the widgets in the 'fRbuttons' frame. ## ## For HEIGHT, allow for the stacked frames: ## 2 chars high for the '.fRbuttons' frame, ## at least 50 pixels high for the '.fRcanvas' frame. ##+##################################################################### ## FOR MIN-WIDTH: set minWidthPx [font measure fontTEMP_varwidth \ " Exit Help Refresh Small Medium Large"] ## We add some pixels to account for right-left-size of ## window-manager decoration (~8 pixels) and some pixels for ## frame/widget borders (~6 widgets x 4 pixels/widget = 24 pixels). set minWinWidthPx [expr {32 + $minWidthPx}] ## For MIN-HEIGHT --- for ## 1 char high for 'fRbuttons' ## 2 chars high for 'fRtheme1' ## 1 char high for 'fRtheme2' ## 2 chars high for 'fRsamprate' ## 50 pixels high for 'fRcanvases' set charHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {6 * $charHeightPx}] ## Add about 50 pixels for height of the canvas ## AND add about 20 pixels for top-bottom window decoration -- ## and some pixels for top-and-bottom of frame/widget borders ## (~4 widgets x 4 pixels/widget = 16 pixels). set minWinHeightPx [expr {86 + $minWinHeightPx}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## We may allow the window to be resizable. We pack the canvases ## (and the frames that contain them) with '-fill both -expand 1' ## so that the canvases 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 ##+############################################################## ## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI. ## NOTE: This can aid INTERNATIONALIZATION. This array can ## be set according to a nation/region parameter. ##+############################################################## ## if { "$VARlocale" == "en"} ## For '.fRbuttons' frame: set aRtext(buttonEXIT) "Exit" set aRtext(buttonHELP) "Help" set aRtext(buttonREFRESH) "Refresh" set aRtext(radbuttSMALL) "S" set aRtext(radbuttMEDIUM) "M" set aRtext(radbuttLARGE) "L" ## For '.fRtheme1' frame: set aRtext(labelTHEMEscale) "Theme for the meters :" set aRtext(chkbuttLABELS) "DrawTicLabels" ## For '.fRtheme2' frame: set aRtext(chkbuttARC) "DrawTicsArc" set aRtext(chkbuttCORNERS) "ShowCornerFeatures (if in theme)" ## For '.fRsamprate' frame: set aRtext(labelSAMPLEscale) "SampleRate (seconds) :" set aRtext(labelSAMPLECOUNT) "Samples:" ## END OF if { "$VARlocale" == "en"} ##+############################################################## ## DEFINE THE AVAILABLE THEMES, VIA SEVERAL ARRAYS. ## ## Each theme (indexed via an integer) includes the following items, ## stored in Tcl arrays: ## - a BACKGROUND IMAGE (a GIF or PNG file) - and we allow for a ## small, medium, and large background images (about 200, 350, ## and 500 pixels high) ## - a BULLET/POINTER IMAGE (a small transparent GIF or PNG file) ## - a RIVET IMAGE (a small transparent GIF or PNG file), which ## is shown optionally, according to a checkbutton on the GUI ## (If null, of course it is not available to be shown.) ## - some parameters, such as ## - the CENTER LOCATION on the background image (in case it is ## not symmetric) --- typically about 0.5,0.5 ## - a RADIUS for an arc, for positioning tic marks and tic mark ## labels --- as well as for positioning the 'bullet' image. ## (Typically this radius value will be about 0.35.) ## - an indicator of whether the tic marks & labels should be ## PLACED ON THE INSIDE OR THE OUTSIDE OF THE ARC. (This ## indicator is set to 'in' or 'out'.) ## - an indicator of WHETHER TIC MARKS ARE TO BE DRAWN/PLACED. ## Tic marks are not needed if they are already on the ## background image. ## - START and EXTENT angles for the arc, such as -70 and 320. ## (The arc is drawn counter-clockwise, but we use it in a ## clockwise fashion.) ## ##+############################################################## ## Theme 1: set aRimgBkgd(1) "meter_background_A07_blue-blackDisk_blue-hilitedEdge_blackBkgd" set aRticsYorN(1) "N" set aRcenterX(1) 0.5 set aRcenterY(1) 0.5 set aRradiusArc(1) 0.35 set aRinORout(1) "in" set aRhexcolorText(1) "#ffffff" set aRhexcolorTics(1) "#ffffff" set aRimgPointer(1) "bullet_earth_whiteOnBlue_transp_25x25" set aRimgCornerFeature(1) "screwheadPhillips_shinyMetal_transp_32x32" set aRstartAngle(1) -70 set aRextentAngle(1) 320 ## Theme 2: set aRimgBkgd(2) "meter_background_A08_blueShadedDisk_blackShadedEdge_checkeredBkgd" set aRticsYorN(2) "N" set aRcenterX(2) 0.5 set aRcenterY(2) 0.5 set aRradiusArc(2) 0.27 set aRinORout(2) "in" set aRhexcolorText(2) "#ffffff" set aRhexcolorTics(2) "#ffffff" set aRimgPointer(2) "bullet_orangeGlossy_transp_25x25" set aRimgCornerFeature(2) "" set aRstartAngle(2) -44 set aRextentAngle(2) 268 ## Theme 3: set aRimgBkgd(3) "meter_background_A11_circularlyPolishedMetal_darkerVersion" set aRticsYorN(3) "N" set aRcenterX(3) 0.5 set aRcenterY(3) 0.5 set aRradiusArc(3) 0.30 set aRinORout(3) "in" set aRhexcolorText(3) "#ffffff" set aRhexcolorTics(3) "#ffffff" set aRimgPointer(3) "bullet_8ball_transp_25x25" set aRimgCornerFeature(3) "" set aRstartAngle(3) -70 set aRextentAngle(3) 320 ## Theme 4: set aRimgBkgd(4) "meter_background_A13_darkBlueGridDisk_off-whiteEdge_whiteBkgd_SPEAKERlike" set aRticsYorN(4) "N" set aRcenterX(4) 0.5 set aRcenterY(4) 0.5 set aRradiusArc(4) 0.30 set aRinORout(4) "out" set aRhexcolorText(4) "#ffffff" set aRhexcolorTics(4) "#ffffff" set aRimgPointer(4) "bullet_greenSquare_transp_21x21" set aRimgCornerFeature(4) "" set aRstartAngle(4) -70 set aRextentAngle(4) 320 ## Theme 5: set aRimgBkgd(5) "meter_background_A20_lightBlueShadedDisk_off-whiteEdge_whiteBkgd" set aRticsYorN(5) "N" set aRcenterX(5) 0.51 set aRcenterY(5) 0.48 set aRradiusArc(5) 0.25 set aRinORout(5) "in" set aRhexcolorText(5) "#000000" set aRhexcolorTics(5) "#000000" set aRimgPointer(5) "bullet_8ball_transp_25x25" set aRimgCornerFeature(5) "" set aRstartAngle(5) -70 set aRextentAngle(5) 320 ## Theme 6: set aRimgBkgd(6) "meter_background_A22_metallicGear_whiteBkgd" set aRticsYorN(6) "N" set aRcenterX(6) 0.499 set aRcenterY(6) 0.50 set aRradiusArc(6) 0.31 set aRinORout(6) "out" set aRhexcolorText(6) "#000000" set aRhexcolorTics(6) "#000000" set aRimgPointer(6) "bullet_earth_whiteOnBlue_transp_25x25" set aRimgCornerFeature(6) "" set aRstartAngle(6) -70 set aRextentAngle(6) 320 ## Theme 7: set aRimgBkgd(7) "meter_background_A24_redShadedSphere_noEdge_grayBkgd" set aRticsYorN(7) "N" set aRcenterX(7) 0.5 set aRcenterY(7) 0.5 set aRradiusArc(7) 0.35 set aRinORout(7) "out" set aRhexcolorText(7) "#ffffff" set aRhexcolorTics(7) "#ffffff" set aRimgPointer(7) "bullet_greenShaded_transp_24x24" set aRimgCornerFeature(7) "rivetGrooved_transp_25x25" set aRstartAngle(7) -70 set aRextentAngle(7) 320 ## Theme 8: set aRimgBkgd(8) "meter_background_A28_LibertyOneDollar_Front_2014_InGoodWeTrust" set aRticsYorN(8) "N" set aRcenterX(8) 0.50 set aRcenterY(8) 0.50 set aRradiusArc(8) 0.35 set aRinORout(8) "in" set aRhexcolorText(8) "#000000" set aRhexcolorTics(8) "#000000" set aRimgPointer(8) "bullet_greenShaded_transp_24x24" set aRimgCornerFeature(8) "" set aRstartAngle(8) -70 set aRextentAngle(8) 320 ## Theme 9: set aRimgBkgd(9) "meter_background_A42_silveryRoundedSquare_whiteBkgd" set aRticsYorN(9) "N" set aRcenterX(9) 0.5 set aRcenterY(9) 0.5 set aRradiusArc(9) 0.35 set aRinORout(9) "in" set aRhexcolorText(9) "#000000" set aRhexcolorTics(9) "#000000" set aRimgPointer(9) "bullet_redHilited_transp_25x25" set aRimgCornerFeature(9) "" set aRstartAngle(9) -70 set aRextentAngle(9) 320 ## Theme 10: set aRimgBkgd(10) "meter_background_B03_blackSolidDisk_grayShadedEdge_whiteBkgd" set aRticsYorN(10) "N" set aRcenterX(10) 0.50 set aRcenterY(10) 0.50 set aRradiusArc(10) 0.30 set aRinORout(10) "in" set aRhexcolorText(10) "#ffffff" set aRhexcolorTics(10) "#ffffff" set aRimgPointer(10) "bullet_lightbulb_transp_25x25" set aRimgCornerFeature(10) "screwheadHorizSlot_1_transp_28x28" set aRstartAngle(10) -70 set aRextentAngle(10) 320 ## This 'Nthemes' variable will be used to define the 'samprate' scale. set Nthemes 10 ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : '.fRbuttons' , '.fRtheme1', '.fRtheme2', ## '.fRsamprate', '.fRcanvases' ## ## Sub-frames: '.fRcanvases.fRcanvas1' '.fRcanvases.fRcanvas2' ##+################################################################ ## FOR TESTING change 0 to 1: ## (Example1: To see appearance of frames when borders are drawn.) ## (Example2: To see sizes of frames for various '-fill' options.) ## (Example3: To see how frames expand as window is resized.) if {0} { set BDwidth_frame 2 set RELIEF_frame raised } else { set BDwidth_frame 0 set RELIEF_frame flat } frame .fRbuttons -relief $RELIEF_frame -bd $BDwidth_frame frame .fRtheme1 -relief $RELIEF_frame -bd $BDwidth_frame frame .fRtheme2 -relief $RELIEF_frame -bd $BDwidth_frame # frame .fRsamprate -relief $RELIEF_frame -bd $BDwidth_frame frame .fRsamprate -relief raised -bd 2 frame .fRcanvases -relief $RELIEF_frame -bd $BDwidth_frame frame .fRcanvases.fRcanvas1 -relief raised -bd 2 frame .fRcanvases.fRcanvas2 -relief raised -bd 2 ##+############################## ## PACK the FRAMES. ##+############################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRtheme1 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRtheme2 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRsamprate \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcanvases \ -side top \ -anchor nw \ -fill both \ -expand 1 pack .fRcanvases.fRcanvas1 \ -side left \ -anchor nw \ -fill both \ -expand 1 pack .fRcanvases.fRcanvas2 \ -side right \ -anchor ne \ -fill both \ -expand 1 ##+########################################################## ## The FRAMES ARE PACKED. START PACKING WIDGETS IN THE FRAMES. ##+########################################################## ##+########################################################## ## In FRAME '.fRbuttons' - ## DEFINE several 'BUTTON' widgets ## --- Exit, Help, Refresh --- and 3 RADIOBUTTON widgets. ## THEN PACK THEM. ##+########################################################## button .fRbuttons.buttEXIT \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .fRbuttons.buttHELP \ -text "$aRtext(buttonHELP)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {popup_msgVarWithScroll .topHelp "$HELPtext"} button .fRbuttons.buttREFRESH \ -text "$aRtext(buttonREFRESH)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {Refresh} ## DEFINE Radiobuttons for Small/Medium/Large meters (background images) : radiobutton .fRbuttons.radbuttSMALL \ -text "$aRtext(radbuttSMALL)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARsizeSML \ -value "SMALL" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd $BDwidthPx_radbutt radiobutton .fRbuttons.radbuttMEDIUM \ -text "$aRtext(radbuttMEDIUM)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARsizeSML \ -value "MEDIUM" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd $BDwidthPx_radbutt radiobutton .fRbuttons.radbuttLARGE \ -text "$aRtext(radbuttLARGE)" \ -font fontTEMP_varwidth \ -anchor w \ -variable RADVARsizeSML \ -value "LARGE" \ -selectcolor "$radbuttBKGD" \ -relief flat \ -bd $BDwidthPx_radbutt ## Set the default value for this set of radiobuttons. set RADVARsizeSML "SMALL" ## Pack the widgets in frame '.fRbutton'. pack .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ .fRbuttons.buttREFRESH \ .fRbuttons.radbuttSMALL \ .fRbuttons.radbuttMEDIUM \ .fRbuttons.radbuttLARGE \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRtheme1' - ## DEFINE-and-PACK a LABEL, a SCALE, and 1 CHECKBUTTON. ##+########################################################## label .fRtheme1.labelTHEME \ -text "$aRtext(labelTHEMEscale)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set THEMEindex 1 scale .fRtheme1.scaleTHEME \ -from 1 -to $Nthemes \ -resolution 1 \ -font fontTEMP_SMALL_varwidth \ -variable THEMEindex \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length 100 \ -width $scaleThicknessPx set LABELS0or1 1 checkbutton .fRtheme1.chkbuttLABELS \ -text "$aRtext(chkbuttLABELS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_chkbutton \ -pady $PADYpx_chkbutton \ -bd $BDwidthPx_chkbutt \ -variable LABELS0or1 \ -selectcolor "$chkbuttBKGD" \ -relief flat ## Pack the widgets in the '.fRtheme1' frame: pack .fRtheme1.labelTHEME \ .fRtheme1.scaleTHEME \ .fRtheme1.chkbuttLABELS \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRtheme2' - ## DEFINE-and-PACK 2 CHECKBUTTONS. ##+########################################################## set ARC0or1 1 checkbutton .fRtheme2.chkbuttARC \ -text "$aRtext(chkbuttARC)" \ -font fontTEMP_varwidth \ -padx $PADXpx_chkbutton \ -pady $PADYpx_chkbutton \ -bd $BDwidthPx_chkbutt \ -variable ARC0or1 \ -selectcolor "$chkbuttBKGD" \ -relief flat set CORNERS0or1 1 checkbutton .fRtheme2.chkbuttCORNERS \ -text "$aRtext(chkbuttCORNERS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_chkbutton \ -pady $PADYpx_chkbutton \ -bd $BDwidthPx_chkbutt \ -variable CORNERS0or1 \ -selectcolor "$chkbuttBKGD" \ -relief flat ## Pack the widgets in the '.fRtheme2' frame: pack .fRtheme2.chkbuttARC \ .fRtheme2.chkbuttCORNERS \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRsamprate' - ## DEFINE a LABEL and SCALE WIDGET --- and 2 more LABEL widgets. ## THEN PACK THEM. ##+########################################################## label .fRsamprate.labelSAMPLEscale \ -text "$aRtext(labelSAMPLEscale)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set WAITseconds 60 scale .fRsamprate.scaleSECONDS \ -from 0.1 -to 120.0 \ -resolution 0.1 \ -font fontTEMP_SMALL_varwidth \ -variable WAITseconds \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length $initScaleLengthPx \ -width $scaleThicknessPx ## Here is a label to show the current sample count. label .fRsamprate.labelSAMPLECOUNT \ -text "$aRtext(labelSAMPLECOUNT)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd 0 label .fRsamprate.labelCOUNT \ -textvariable VARsampcnt \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd 0 ## Pack the widgets in frame '.fRsamprate'. pack .fRsamprate.labelSAMPLEscale \ .fRsamprate.scaleSECONDS \ .fRsamprate.labelSAMPLECOUNT \ .fRsamprate.labelCOUNT \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRcanvases.fRcanvas1' - ## DEFINE-and-PACK TWO LABELs and ## ONE CANVAS WIDGET (no scrollbars). ## ## We highlightthickness & borderwidth of the canvas to ## zero, as suggested on page 558, Chapter 37, 'The Canvas ## Widget', in the 4th edition of the book 'Practical ## Programming in Tcl and Tk'. ##+####################################################### label .fRcanvases.fRcanvas1.labelINFO1 \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label label .fRcanvases.fRcanvas1.labelINFO2 \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label canvas .fRcanvases.fRcanvas1.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 ## Pack the widgets in frame '.fRcanvases.fRcanvas1'. pack .fRcanvases.fRcanvas1.labelINFO1 \ .fRcanvases.fRcanvas1.labelINFO2 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcanvases.fRcanvas1.can \ -side top \ -anchor nw \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRcanvases.fRcanvas2' - ## DEFINE-and-PACK TWO LABELs and ## ONE CANVAS WIDGET (no scrollbars). ## ## We highlightthickness & borderwidth of the canvas to ## zero, as suggested on page 558, Chapter 37, 'The Canvas ## Widget', in the 4th edition of the book 'Practical ## Programming in Tcl and Tk'. ##+####################################################### label .fRcanvases.fRcanvas2.labelINFO1 \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label label .fRcanvases.fRcanvas2.labelINFO2 \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label canvas .fRcanvases.fRcanvas2.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 ## Pack the widgets in frame '.fRcanvases.fRcanvas2'. pack .fRcanvases.fRcanvas2.labelINFO1 \ .fRcanvases.fRcanvas2.labelINFO2 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcanvases.fRcanvas2.can \ -side top \ -anchor nw \ -fill none \ -expand 0 ##+################################################## ## END OF DEFINITION of the GUI widgets. ##+################################################## ## Start of BINDINGS, PROCS, Added-GUI-INIT sections. ##+################################################## ##+################################################################## ##+################################################################## ## BINDINGS SECTION: ##+################################################################## bind .fRbuttons.radbuttSMALL {Refresh} bind .fRbuttons.radbuttMEDIUM {Refresh} bind .fRbuttons.radbuttLARGE {Refresh} bind .fRtheme1.scaleTHEME {Refresh} bind .fRtheme1.chkbuttLABELS {Refresh} bind .fRtheme2.chkbuttARC {Refresh} bind .fRtheme2.chkbuttCORNERS {Refresh} ## The Refresh' proc runs both the 'make_meters' proc and the ## 'update_pointers' proc. ## ## In the CHECKBUTTON 'events' above, the theme (in particular, the ## meter background, and size) has not changed, so we do not ## need to rebuild the entire meter. For example, reloading the ## meter background image is not really necessary. (The same ## can be said of the 'entryCPU' event.) ## ## But the use of these checkbuttons is very seldom, if at all, ## in a session. So running 'Refresh' for those events is not ## really incurring an inordinate amount of processing. We use ## 'Refresh' for these events, rather than breaking up the ## 'make_meters' proc into a smaller sequence of procs --- or ## adding arguments to the 'make_meters' proc to turn various ## sections of the make-meter processing off and on. ##+################################################################## ##+################################################################## ## DEFINE PROCS SECTION: ## ## 'make_meters' - to draw 2 meters within 2 Tk canvases ## ## 'make_one_meter' - to draw one meter. Called by 'make_meters' ## to make the 2 meters. ## ## 'update_pointers' - to update the 2 pointers on the 2 meters. Called in the ## 'Additional GUI Initialization' section at the ## bottom of this script. Also called by itself, in ## an 'after $WAITmillisecs' statement. ## And called in the 'Refresh' proc. ## ## 'update_one_pointer' - to draw one pointer in a specified canvas. Called by ## 'update_pointers' to update the 2 pointers on the 2 meters. ## ## 'Refresh' - called by the 'Refresh' button. Also called by ## several bindings in the BINDINGS section above. ## Runs the procs 'make_meters' and 'update_pointers'. ## ## 'popup_msgVarWithScroll' - to show the HELPtext var. Called by the 'Help' button. ## ##+################################################################# ##+######################################################################## ## PROC 'make_meters' ##+######################################################################## ## PURPOSE: Draws all features of 2 'high-definition' meters (except the ## pointers). ## ## The size of the meters is determined by the theme-index and ## the setting of the Small-Medium-Large radiobuttons --- ## which determines which background image is loaded. ## ## CALLED BY: once, at the 'Additional GUI Initialization' section, ## at the bottom of this script --- and in the 'Refresh' proc ## which is called, for example, after the meter is resized ## via the S-M-L radiobuttons. ##+######################################################################## proc make_meters {} { ## FOR TESTING: (to dummy out this proc) # return ######################################################### ## Draw meter1 (without pointer). ######################################################### make_one_meter .fRcanvases.fRcanvas1.can ######################################################### ## Draw meter2 (without pointer). ######################################################### make_one_meter .fRcanvases.fRcanvas2.can } ## END OF proc 'make_meters' ##+######################################################################## ## PROC 'make_one_meter' ##+######################################################################## ## PURPOSE: Places/draws all features of a high-definition meter (except ## the 'pointer'). ## ## The features include: ## 1) the meter background image - with canvas 'create image' ## 2) tic-marks following an arc path - with canvas 'create line' ## 3) optionally (according to a checkbutton setting), an arc ## along the tic-marks - with canvas 'create arc' ## 4) optionally (according to a checkbutton setting), labels ## for the tic-marks - with canvas 'create text' ## 5) optionally (according to a checkbutton setting), decorative ## 'features' (rivets or whatever) at the 4 corners of the ## meter background image. ## ## The theme arrays (in particular, the dimensions of the meter ## background image and the 'center point' and 'radius' ratio-parameters) ## are used to set top-right and bottom-left coordinates to specify ## the location of the arc --- along which tic-marks and labels ## are drawn. ## ## CALLED BY: proc 'make_meters' ##+####################################################################### ## The following variables are used in the 'make_one_meter' proc. ## We set them at the bottom of this script, in the ## 'Additonal GUI Initialization' section, along with other parameter ## settings that the user may want to change. # set pi [expr {4.0 * atan(1.0)}] # set radsPERdeg [expr {$pi/180.0}] # set Nsegs 10 # set ticLabels "0 10 20 30 40 50 60 70 80 90 100" proc make_one_meter {canvas} { ## FOR TESTING: (to dummy out this proc) # return global pi radsPERdeg Nsegs ticLabels THEMEindex \ aRimgBkgd aRimgPointer aRimgCornerFeature \ aRcenterX aRcenterY aRradiusArc aRinORout aRticsYorN \ aRhexcolorText aRhexcolorTics aRstartAngle aRextentAngle \ RADVARsizeSML ARC0or1 LABELS0or1 CORNERS0or1 \ imgSUFFIX DIRmeterBackgrounds DIRpointers DIRcornerFeatures \ centerXpx centerYpx radiusPx ################################################################ ## REMOVE any previously placed ELEMENTS IN THIS CANVAS, if any. ################################################################ catch {$canvas delete all} ################################################################## ## CREATE THE TK IN-MEMORY IMAGE STRUCTURE FROM THE ## METER-BACKGROUND-IMAGE FILE --- determined by the current ## theme index, $THEMEindex. ################################################################## image create photo imgID_meter -file \ "$DIRmeterBackgrounds/$aRimgBkgd($THEMEindex)_${RADVARsizeSML}.$imgSUFFIX" ################################################## ## PUT THE BACKGROUND IMAGE ON THE CANVAS. ################################################## $canvas create image 0 0 -anchor nw -image imgID_meter ## FOR TESTING: (exit this proc before adding more to the meter) # return ################################################################## ## CREATE THE TK IN-MEMORY IMAGE STRUCTURE FROM THE ## POINTER-IMAGE FILE --- determined by the current ## theme index, $THEMEindex --- IF the file is specified in ## the theme. ################################################################## if {"$aRimgPointer($THEMEindex)" != ""} { image create photo imgID_pointer -file \ "$DIRpointers/$aRimgPointer($THEMEindex).$imgSUFFIX" } ################################################################## ## Start setting parameters that may be used to place/draw other ## items on the meter --- tic-marks, arc, tic-labels, ## a-feature-in-4-corners, meter-title. ################################################################## ## GET the WIDTH AND HEIGHT OF THE BACKGROUND IMAGE for the meter. ################################################################## set meterWidthPx [image width imgID_meter] set meterHeightPx [image height imgID_meter] set meterMinPx $meterWidthPx if {$meterHeightPx < $meterMinPx} {set meterMinPx $meterHeightPx} ################################################################## ## FORCE THE CANVAS TO CHANGE SIZE ACCORDING TO THE NEW METER IMAGE. ################################################################## $canvas configure -width $meterWidthPx $canvas configure -height $meterHeightPx ########################################################### ## SET parameters for drawing TIC-MARKS and/or LABELS ## around the ARC. ########################################################### ## NOTE: centerXpx centerYpx radiusPx are calculated in this proc. ## They are also used in the 'update_one_pointer' proc. ## ## Since the same meter-background-image with the ## same center-location and radius-for-tic-marks parameters ## are used for both meters, we declare these 3 parameters ## as global parameters and use them in the ## 'update_one_pointer' proc, for both meters. ########################################################### set centerXpx [expr {$meterWidthPx * $aRcenterX($THEMEindex)}] set centerYpx [expr {$meterHeightPx * $aRcenterY($THEMEindex)}] set radiusPx [expr {$meterMinPx * $aRradiusArc($THEMEindex)}] set DEGperTIC [expr {double($aRextentAngle($THEMEindex)) / $Nsegs}] ########################################################### ## DRAW TIC-MARKS around the meter. ## (The ticmarks are drawn according to the value of the ## array setting $aRticsYorN($THEMEindex).) ########################################################### if { "$aRticsYorN($THEMEindex)" == "N" } { ## Set the outer or inner location (radius) of the TICMARKS, ## according to the value of $aRinORout($THEMEindex). if { "$aRinORout($THEMEindex)" == "in" } { set radius2Px [expr {0.90 * $radiusPx}] } if { "$aRinORout($THEMEindex)" == "out" } { set radius2Px [expr {1.10 * $radiusPx}] } set angle0 [expr {$aRextentAngle($THEMEindex) + $aRstartAngle($THEMEindex)}] for {set i 0} {$i <= $Nsegs} {incr i} { set rads [expr {($angle0 - ($DEGperTIC * $i)) * $radsPERdeg}] set x1 [expr {$centerXpx + $radiusPx * cos($rads)}] set y1 [expr {$centerYpx - $radiusPx * sin($rads)}] set x2 [expr {$centerXpx + $radius2Px * cos($rads)}] set y2 [expr {$centerYpx - $radius2Px * sin($rads)}] $canvas create line \ $x1 $y1 $x2 $y2 \ -fill $aRhexcolorTics($THEMEindex) -width 2 } ## END OF for {set i 0} ... (for drawing TIC MARKS) } ## END OF if { "$aRticsYorN($THEMEindex)" == "N" } ## FOR TESTING: (exit this proc before adding more to the meter) # return ########################################################### ## DRAW LABELS around the meter. ## (The labels are drawn if the LABELS check button is ON.) ########################################################### if {$LABELS0or1 == 1} { ## Set the outer or inner location (radius) of the LABELS, ## according to the value of $aRinORout($THEMEindex). if { "$aRinORout($THEMEindex)" == "in" } { set radius2Px [expr {0.80 * $radiusPx}] } if { "$aRinORout($THEMEindex)" == "out" } { set radius2Px [expr {1.20 * $radiusPx}] } set angle0 [expr {$aRextentAngle($THEMEindex) + $aRstartAngle($THEMEindex)}] for {set i 0} {$i <= $Nsegs} {incr i} { set rads [expr {($angle0 - ($DEGperTIC * $i)) * $radsPERdeg}] ## Calculate a center point for the i-th label. set x1 [expr {$centerXpx + $radius2Px * cos($rads)}] set y1 [expr {$centerYpx - $radius2Px * sin($rads)}] ## Get the i-th label from the list of labels. set ticlabel [lindex $ticLabels $i] ## Draw the tic-label on the canvas. $canvas create text \ $x1 $y1 \ -anchor center -justify center \ -fill $aRhexcolorText($THEMEindex) \ -text "$ticlabel" -font { Helvetica 10 } } ## END OF for {set i 0} ... (for drawing TIC LABELS) } ## END OF if {$LABELS0or1 == 1} ## FOR TESTING: (exit this proc before adding more to the meter) # return ################################################# ## IF the ARC checkbutton is ON. ## DRAW ARC-LINE along which to put tic marks, ################################################# ## EXAMPLE ## (if aRextentAngle($THEMEindex) = 320 degrees ## and aRstartAngle($THEMEindex) = -70 degrees): ## ## 320 degrees counter-clockwise from -70 degrees ## (based at 3 oclock) is 70 degrees beyond 180. ## I.e. -70 + 320 = 250 = 180 + 70 ################################################# if {$ARC0or1 == 1} { set x1 [expr {$centerXpx - $radiusPx}] set y1 [expr {$centerYpx - $radiusPx}] set x2 [expr {$centerXpx + $radiusPx}] set y2 [expr {$centerYpx + $radiusPx}] $canvas create arc $x1 $y1 $x2 $y2 \ -start $aRstartAngle($THEMEindex) \ -extent $aRextentAngle($THEMEindex) -style arc \ -outline $aRhexcolorTics($THEMEindex) -width 2 } ## END OF if {$ARC0or1 == 1} ## FOR TESTING: (exit this proc before adding more to the meter) # return ############################################################# ## IF the CORNERS checkbutton is ON, ## PLACE 4 'FEATURES' AT THE CORNERS of the meter background. ############################################################# if {$CORNERS0or1 == 1 && "$aRimgCornerFeature($THEMEindex)" != ""} { ################################################################## ## CREATE THE TK IN-MEMORY IMAGE STRUCTURE FROM THE ## CORNER-FEATURE-IMAGE FILE --- determined by the current ## theme index, $THEMEindex. ################################################################## image create photo imgID_feature -file \ "$DIRcornerFeatures/$aRimgCornerFeature($THEMEindex).$imgSUFFIX" ############################################################### ## We set the corner coordinates of the 'outside-corner' of ## the 4 features at a fixed offset. ## ## (We could change this to position the 4 features based on ## its image dimensions and the width and height of the ## meter background.) ############################################################### ## Get the current imgID_feature width and height. set featureWidthPx [image width imgID_feature] set featureHeightPx [image height imgID_feature] # set featureMaxPx $featureWidthPx # if {$featureHeightPx > $featureMaxPx} {set featureMaxPx $featureHeightPx} # set FEATUREoffsetPx 8 set FEATUREoffsetPx [expr {0.02 * $meterMinPx}] ## Place upper-left feature. set x1 $FEATUREoffsetPx set y1 $FEATUREoffsetPx $canvas create image $x1 $y1 \ -anchor nw -image imgID_feature ## Place upper-right feature. set x1 [expr {$meterWidthPx - $FEATUREoffsetPx}] set y1 $FEATUREoffsetPx $canvas create image $x1 $y1 \ -anchor ne -image imgID_feature ## Place lower-left feature. set x1 $FEATUREoffsetPx set y1 [expr {$meterHeightPx - $FEATUREoffsetPx}] $canvas create image $x1 $y1 \ -anchor sw -image imgID_feature ## Place lower-right feature. set x1 [expr {$meterWidthPx - $FEATUREoffsetPx}] set y1 [expr {$meterHeightPx - $FEATUREoffsetPx}] $canvas create image $x1 $y1 \ -anchor se -image imgID_feature } ## END OF if {$CORNERS0or1 == 1} ## FOR TESTING: (exit this proc before adding more to the meter) # return #################################################################### ## DRAW A '%USED' TEXT-LABEL, ## near the top of the meter arc. #################################################################### $canvas create text \ $centerXpx [expr {$centerYpx - (0.45 * $radiusPx)}] \ -anchor center -justify center \ -fill $aRhexcolorText($THEMEindex) \ -text "%used" -font { Helvetica 10 normal roman} } ## END OF proc 'make_one_meter' ##+######################################################################## ## PROC 'update_pointers' ##+######################################################################## ## PURPOSE: Updates the 2 pointers on the 2 canvases --- using the ## Linux/Unix/BSD/Mac 'free' command to get ## MEMtot, MEMused, SWAPtot, SWAPused (in Megabytes). ## ## CALLED BY: the 'Additional GUI Initialization' section at the ## bottom of this script, and within this proc itself --- ## in the 'after' statement at the bottom of this proc. ##+######################################################################## proc update_pointers {} { global argv0 DIRthisScript WAITseconds VARsampcnt ########################################################## ## Get MEMtot,MEMused,SWAPtot,SWAPused via 'free' command. ########################################################## foreach {MEMtot MEMused SWAPtot SWAPused} \ [exec $DIRthisScript/get_memory_and_swap.sh] {break} ########################################################### ## AN ALTERNATIVE, avoiding use of 'external' shell script. ## (Untested. Something like this should work.) ########################################################### # foreach {MEMtot MEMused SWAPtot SWAPused} \ # [exec {/bin/sh -c "free -m -o | tail -2 | cut -c6-30 | tr '\n' ' ' | sed 's/ */ /g'"}] \ # {break} ######################################################## ## ANOTHER ALTERNATIVE, suggested by RLE of wiki.tcl.tk, ## to avoid use of 'external' shell script. ######################################################## # set freeinfo [ exec free -m -o ] # regexp {Mem: +([0-9]+) +([0-9]+).*Swap: +([0-9]+) +([0-9]+)} $freeinfo -> MEMtot MEMused SWAPtot SWAPused ## FOR TESTING: # puts " MEMtot: $MEMtot MEMused: $MEMused" # puts "SWAPtot: $SWAPtot SWAPused: $SWAPused" ################################################## ## Increment the sample count, to show on the GUI. ################################################## incr VARsampcnt ######################################################### ## Update the 2 pointers. ######################################################### ## FOR TESTING: (hardcoded meter values) # update_one_pointer .fRcanvases.fRcanvas1 2000 650 Memory # update_one_pointer .fRcanvases.fRcanvas2 1000 0 Swap update_one_pointer .fRcanvases.fRcanvas1 $MEMtot $MEMused Memory update_one_pointer .fRcanvases.fRcanvas2 $SWAPtot $SWAPused Swap ############################################################ ## Force the pointers to show up on the GUI. ## (Needed???) It appears NOT. ############################################################ # update #################################################################### ## 'Pseudo-Recursively' 'fork off' another (delayed) instance of the ## 'update_pointers' here to support the wait-seconds scale widget ## --- using the 'after ms cmd arg arg ...' form of the 'after' ## command. ## (We recalculate WAITmillisecs in case the user has changed the ## WAITseconds value via the 'samprate' scale widget.) #################################################################### set WAITmillisecs [expr {int($WAITseconds * 1000)}] after $WAITmillisecs update_pointers } ## END OF proc 'update_pointers' ##+######################################################################## ## PROC 'update_one_pointer' ##+######################################################################## ## PURPOSE: Updates a pointer in a specified canvas --- using the canvas ## frame ID and the 'tot' and 'used' numbers passed as arguments. ## ## CALLED BY: the 'update_pointers' proc ##+######################################################################## proc update_one_pointer {frame TOT USED TYPE} { ## FOR TESTING: (dummy out this routine) # return global pi radsPERdeg Nsegs centerXpx centerYpx radiusPx \ aRextentAngle aRstartAngle aRimgPointer aRinORout THEMEindex ## 'aRcenterX aRcenterY aRradiusArc' ## are not needed. We use the globals 'centerXpx centerYpx radiusPx' ## that were calculated in 'make_one_meter' instead. ################################################################## ## Set the text in the 2 label lines above the canvas-meter-image. ################################################################## set PERcent [expr {($USED * 100.0) / $TOT}] set TOTtext "Total $TYPE = $TOT Megabytes" set USEDtext "Used $TYPE = $USED Megabytes" $frame.labelINFO1 configure -text "$TOTtext" $frame.labelINFO2 configure -text "$USEDtext" ################################################################### ## Use 'PERcent' to locate the pointer around the arc-of-tic-marks. ## To do this: ## Convert PERcent to an angle in radians on the arc. ## ## First, set the angle for the zero-point on the arc-of-tic-marks. ################################################################### set angle0 [expr {$aRextentAngle($THEMEindex) + $aRstartAngle($THEMEindex)}] set degsPointer [expr {$angle0 - ($aRextentAngle($THEMEindex) * $PERcent / 100.0)}] set radsPointer [expr {$degsPointer * $radsPERdeg}] ## FOR TESTING: # puts "PERcent: $PERcent degsPointer: $degsPointer radsPointer: $radsPointer" ##################################### ## Remove a previous pointer, if any. ##################################### catch {$frame.can delete -tags TAGpointer} ################################################## ## If an image was supplied for a pointer, ## PUT THE POINTER-IMAGE ON THE CANVAS --- ## otherwise draw a red pointer 'needle' with ## canvas 'create line'. ################################################## if {"$aRimgPointer($THEMEindex)" != ""} { ################################################################ ## Calculate the coordinates for the tip and base of the ## IMAGE pointer --- according to whether aRinORout is ## "in" or "out". ################################################################ if {"$aRinORout($THEMEindex)" == "in"} { set x1 [expr {$centerXpx + 0.60 * $radiusPx*cos($radsPointer)}] set y1 [expr {$centerYpx - 0.60 * $radiusPx*sin($radsPointer)}] } else { set x1 [expr {$centerXpx + 1.35 * $radiusPx*cos($radsPointer)}] set y1 [expr {$centerYpx - 1.35 * $radiusPx*sin($radsPointer)}] } $frame.can create image $x1 $y1 -anchor center \ -image imgID_pointer -tag TAGpointer } else { ################################################################ ## Calculate the coordinates for the tip and base of the ## NEEDLE pointer. ################################################################ set xtip [expr {$centerXpx + 0.60 * $radiusPx*cos($radsPointer)}] set ytip [expr {$centerYpx - 0.60 * $radiusPx*sin($radsPointer)}] set xbase $centerXpx set ybase $centerYpx #################################################################### ## Draw a red-line pointer and a reddish-white line on either side ## --- for an (attempted) anti-aliasing effect. ## ## NOTE: This attempt at anti-aliasing did not work out well. ## This code needs improvement --- or simply one 'create line'. #################################################################### $frame.can create line \ $xbase $ybase $xtip $ytip \ -fill #ff0000 -width 4 -tag TAGpointer $frame.can create line \ [expr {$xbase + 1}] [expr {$ybase + 1}] \ [expr {$xtip + 1}] [expr {$ytip + 1}] \ -fill #ff8888 -width 2 -tag TAGpointer $frame.can create line \ [expr {$xbase - 1}] [expr {$ybase - 1}] \ [expr {$xtip - 1}] [expr {$ytip - 1}] \ -fill #ff8888 -width 2 -tag TAGpointer } ## END OF if {"$aRimgPointer" != ""} } ## END OF proc 'update_one_pointer' ##+############################################################# ## proc Refresh ## ## PURPOSE: 'Refresh' the two meters and their pointers --- when ## the user wants to set a new start-point of mem-swap values ## and/or when the user changes the meter features. ## ## CALLED BY: 'Refresh' button ##+############################################################# proc Refresh {} { ## Cancel pending pointer update(s), before redrawing ## the meters and restarting the update-pointers cycle. set LISTids [after info] foreach ID $LISTids { after cancel $ID } make_meters update_pointers } ## END OF proc 'Refresh' ##+######################################################################## ## 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 a filename entry field ## or clicking on a radiobutton. ## ## For a similar proc with focus-grab-tkwait added, ## see the proc 'popup_msgVarWithScroll_wait' in a ## 3DterrainGeneratorExaminer Tk script. ## ## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk', ## 4th edition, by Welch, Jones, Hobbs. ## ## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg) ## and a variable holding text (many lines, if needed). ## ## CALLED BY: 'help' button ##+######################################################################## ## To have more control over the formatting of the message (esp. ## words per line), we use this 'toplevel-text' method, ## rather than the 'tk_dialog' method -- like on page 574 of the book ## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications ## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor". ##+######################################################################## proc popup_msgVarWithScroll { toplevName VARtext } { ## global fontTEMP_varwidth #; Not needed. 'wish' makes this global. ## global env # bell # bell ################################################# ## Set VARwidth & VARheight from $VARtext. ################################################# ## To get VARheight, ## split at '\n' (newlines) and count 'lines'. ################################################# set VARlist [ split $VARtext "\n" ] ## For testing: # puts "VARlist: $VARlist" set VARheight [ llength $VARlist ] ## For testing: # puts "VARheight: $VARheight" ################################################# ## To get VARwidth, ## loop through the 'lines' getting length ## of each; save max. ################################################# set VARwidth 0 ############################################# ## LOOK AT EACH LINE IN THE LIST. ############################################# foreach line $VARlist { ############################################# ## Get the length of the line. ############################################# set LINEwidth [ string length $line ] if { $LINEwidth > $VARwidth } { set VARwidth $LINEwidth } } ## END OF foreach line $VARlist ## For testing: # puts "VARwidth: $VARwidth" ############################################################### ## NOTE: VARwidth works for a fixed-width font used for the ## text widget ... BUT the programmer may need to be ## careful that the contents of VARtext are all ## countable characters by the 'string length' command. ############################################################### ##################################### ## SETUP 'TOP LEVEL' HELP WINDOW. ##################################### catch {destroy $toplevName} toplevel $toplevName # wm geometry $toplevName 600x400+100+50 wm geometry $toplevName +100+50 wm title $toplevName "Note" # wm title $toplevName "Note to $env(USER)" wm iconname $toplevName "Note" ##################################### ## In the frame '$toplevName' - ## DEFINE THE TEXT WIDGET and ## its two scrollbars --- and ## DEFINE an OK BUTTON widget. ##################################### if {$VARheight > 10} { text $toplevName.text \ -wrap none \ -font fontTEMP_varwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" scrollbar $toplevName.scrolly \ -orient vertical \ -command "$toplevName.text yview" scrollbar $toplevName.scrollx \ -orient horizontal \ -command "$toplevName.text xview" } else { text $toplevName.text \ -wrap none \ -font fontTEMP_varwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 } button $toplevName.butt \ -text "OK" \ -font fontTEMP_varwidth \ -command "destroy $toplevName" ############################################### ## PACK *ALL* the widgets in frame '$toplevName'. ############################################### ## Pack the bottom button BEFORE the ## bottom x-scrollbar widget, pack $toplevName.butt \ -side bottom \ -anchor center \ -fill none \ -expand 0 if {$VARheight > 10} { ## Pack the scrollbars BEFORE the text widget, ## so that the text does not monopolize the space. pack $toplevName.scrolly \ -side right \ -anchor center \ -fill y \ -expand 0 ## DO NOT USE '-expand 1' HERE on the Y-scrollbar. ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA. pack $toplevName.scrollx \ -side bottom \ -anchor center \ -fill x \ -expand 0 ## DO NOT USE '-expand 1' HERE on the X-scrollbar. ## THAT KEEPS THE TEXT AREA FROM EXPANDING. pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } else { pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## $toplevName.text delete 1.0 end $toplevName.text insert end $VARtext $toplevName.text configure -state disabled } ## END OF PROC 'popup_msgVarWithScroll' ##+######################## ## END of PROC definitions. ##+######################## ## Set HELPtext var. ##+######################## set HELPtext "\ \ \ ** HELP for this 'Memory and Swap Usage' Monitoring Utility ** This utility is meant to show a GUI that holds a pair of tachometer-style meters. The 'pointers' on the 2 meters are updated periodically --- OR whenever the user chooses to click on the 'Refresh' button. The pointers show the PERCENT of available MEMORY and SWAP resources in use, on this computer. The TOTAL and USED values of those two resources are shown just above the two meters (in Megabytes). This Tcl-Tk script was developed on Linux and uses the 'free' command to get the memory and swap data to determine where to relocate the position of the pointers on the meters. ************ METER RESIZE: The user can choose to resize the meters. The Small/Medium/Large radiobuttons at the top of the GUI allow the user to quickly choose a different size for the 2 meters. ********* TIC-MARKS (and corner features) There are 3 checkbuttons on the GUI that can be used to turn off the placement of - Tic Labels - the Tic Arc - corner-features in the 4 corners of each meter. ************ METER THEMES: About 10 different themes for the meters (including the pointers) are quickly selectable via a 'Theme' scale widget on the GUI. The themes are selected by an integer --- from 1 to 10, say. The themes include 3 images: - a meter background image - a pointer image - an optional 'corner feature' image (such as a rivet in 4 corners). The images are stored in 3 sub-directories of this Tk script: - 'meter_backgrounds' - 'pointers' - 'corner_features' The user can add or change images in those 3 subdirectories. In this Tk script, there are 12 arrays that are used to store data for each 'meter-theme'. The user can add/change a theme by adding/changing some 'set' statements. Here is an example of the array set statements for a theme 1: set aRimgBkgd(1) \"meter_background_3DsphericalPuzzle-whiteBkgd\" set aRticsYorN(1) \"N\" set aRcenterX(1) 0.5 set aRcenterY(1) 0.5 set aRradiusArc(1) 0.35 set aRinORout(1) \"in\" set aRhexcolorText(1) \"#000000\" set aRhexcolorTics(1) \"#000000\" set aRimgPointer(1) \"bullet_earth_whiteOnBlue_transp_25x25\" set aRimgCornerFeature(1) \"screwHeadSlotted_30deg_gray_transp_27x27\" set aRstartAngle(1) -70 set aRextentAngle(1) 320 The 3 images are specified in the arrays named 'aRimgBkgd', 'aRimgPointer', and 'aRimgCornerFeature'. In addition to these image names, there are parameters that indicate: - whether tic marks are on the meter-background image. (If 'N', tic marks may be drawn on the background image.) - the center and radius for the tic-marks arc of the meter (as ratios between 0.0 and 1.0). - whether the tic-marks should be drawn to the inside or outside of the arc ('in' or 'out'). - a color for the tic-marks and the tic-labels (defaulted to black = #000000). - a start-angle and extent-angle for drawing the tic-marks arc (specified relative to a horizontal, right-pointing x-axis). Note that this GUI is very malleable. Not only can the user edit the Tk script and change the themes, but ALSO essentially anything about the GUI is changeable. For example, the fonts used for the text on buttons and labels are changeable. The organization of the elements on the GUI are changeable --- some changes as easy as changing the order of some 'pack' statements. ************************************ THE SCRIPT USED to update the meters: A Tcl 'exec' command calls on a separate shell script --- 'get_memory_and_swap.sh' --- that uses the 'free' command to get the memory and swap data (total and used) and extract-and-format the data for return to this Tk script. If the 'free' command is not available on your computer, you may have to install it --- or edit the shell script to use a different command to get the memory and swap data. *********************** CAPTURING THE GUI IMAGE: A screen/window capture utility (like 'gnome-screenshot' on Linux) can be used to capture the GUI image in a PNG or GIF file, say. If necessary, an image editor (like 'mtpaint' on Linux) can be used to crop the window capture image. The image could also be down-sized --- say to make a smaller image suitable for use in a web page or an email. " ##+################################################################ ##+################################################################ ## Additional GUI INITIALIZATION: Mainly to ## - Set various parameters, esp. ones that the user may want to change. ## - Put the 2 meters on their 2 canvases, with 'make_meters'. ## - Start an execution loop for the 'update_pointers' proc. ##+################################################################ ##+############################################################## ## THE 'THEMES-ARRAYS' are defined far above --- just below the ## section where the 'aRtext' array values are set. ## You can change the themes there, if you wish. ##+############################################################## #+################################################################# ## Set the image type to use --- GIF or PNG. ## If you use the 8.5 or earlier versions of the 'wish' interpreter, ## you may need to use GIF files for the images --- if you do not ## want to use Tcl-Tk 'extensions'. For version 8.6 and later, ## you can use PNG files. ## ## If you change the image type, change the DIR variables below. #+################################################################# set imgSUFFIX "gif" # set imgSUFFIX "png" ##+############################################################# ## Set the directory that this Tk script is in. That will be the ## directory that the 'external' utility shell script should be ## in --- to get the mem-and-swap values via the 'free' command. ## Also this directory is used to set the full directory names ## of the 'meter_backgrounds' 'pointers' and 'corner_features' ## image directories. ##+############################################################# ## FOR TESTING: # puts "argv0: $argv0" # set DIRthisScript "." # set DIRthisScript "[pwd]" # set DIRthisScript "$env(HOME)/apps/tkUtils" set DIRthisScript "[file dirname $argv0]" set DIRmeterBackgrounds "$DIRthisScript/meter_backgrounds/GIFs" set DIRpointers "$DIRthisScript/pointers/GIFs" set DIRcornerFeatures "$DIRthisScript/corner_features/GIFs" ##+################################################### ## Set the scale widget var for initial 'refresh rate' ## (actually wait-time = 'wave-length', not 'frequency') ## --- in seconds. ##+################################################### # set WAITseconds 60.0 ## 60 seconds might be too long if some process suddenly ## starts eating up a lot of memory. 20 seconds, or even ## 10 seconds might be better to use. set WAITseconds 10.0 ## FOR TESTING: # set WAITseconds 1.0 ##+#################################################### ## Initialize the variable we use to keep track of ## the sample count. ##+#################################################### set VARsampcnt 0 ##+#################################################### ## Initialize variables that will be used in the ## 'make_one_meter' and 'update_one_pointer' procs. ##+#################################################### set pi [expr {4.0 * atan(1.0)}] set radsPERdeg [expr {$pi/180.0}] set Nsegs 10 set ticLabels "0 10 20 30 40 50 60 70 80 90 100" ##+################################################# ## Draw the 2 meters (without pointers). ##+################################################# make_meters ##+###################################################### ## Do an initial draw of the pointers. ## ######## ## NOTE: ## ## The proc 'update_pointers' calls (re-schedules) itself, ## with 'after $WAITmillisecs', where WAITmillisecs is ## set from the $WAITseconds 'scale' var. ##+###################################################### update_pointers