#!/usr/bin/wish -f ##+######################################################################## ## ## SCRIPT: meters_netstats_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 ## rate of network activity --- rate of change of RX (received ## packets) and TX (transmitted packets) data. ## ## This script was developed on Linux and uses the 'netstat' ## command (or an alternative command) to periodically get the ## RX and TX 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 (RX and TX). ## ## 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 ## RX and TX rates. 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. ## ## --------------------------------------------------------------- ## NETWORK ACTIVITY - RX and TX Rates [window title] ## --------------------------------------------------------------- ## {Exit} {Help} {Refresh} {Ifaces} eth0_ 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: ## ## 0) There is an ENTRY widget to hold a network interface-ID ## such as eth0 or eth1 or wlan0. ## ## 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 about one second, and up to about ## 30 seconds. ## ## 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 ## RX-rate and Total-RX-packets-so-far and TX-rate and ## Total-TX-packets-so-far, 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 ## ## - 4 button widgets ## - 7 label widgets ## - 3 radiobutton widgets in 1 group ## - 3 checkbutton widgets ## - 2 scale widgets ## - 2 canvas widgets ## - 1 entry widget (for network interface ID) ## - 0 listbox widgets ## ##+#################################### ## METHOD USED to update the two meters: ## ## A Tcl 'exec' command calls on a separate shell script that uses ## the 'netstat' (or other) command to get the RX,TX data and ## extract-and-format the data for return to this Tk script. ## ##+####################### ## 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 ## as well as an entry widget for interface-ID, ## 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 place each pointer. ## ## 'Refresh' - called by 'Refresh' button. Runs 'make_meters' ## and 'update_pointers'. ## ## 'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var. ## ## ## 5) Additional GUI Initialization: ## - call 'make_meters' to put the 2 meters on the canvas ## - call 'update_pointers' --- to initialize the pointer locations. ## ##+####################################################################### ## 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 2015oct31 Started the basic code of the ## script based on my previous ## netactivity script ## meters_net_stats.tk ## which was based on a ## demo script by Marco Maggi ## at http://wiki.tcl.tk/9107 ## Changed by: Blaise Montandon 2015nov09 Finalized set of 10 themes and ## their positioning parameters. ##+######################################################################## ##+###################################################### ## Set WINDOW TITLE and POSITION. ##+###################################################### wm title . "Network Activity - RX and TX Rates" wm iconname . "RX-TX" wm geometry . +15+30 ##+###################################################### ## Set the COLOR SCHEME for the window and its widgets --- ## such as entry field and scale-trough background color. ##+###################################################### tk_setPalette "#e0e0e0" set entryBKGD "#ffffff" set scaleBKGD "#f0f0f0" set radbuttBKGD "#f0f0f0" set chkbuttBKGD "#f0f0f0" # 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 ## ENTRY widget geom settings: set BDwidthPx_entry 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 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 Ifaces eth0 S M L"] ## We add some pixels to account for right-left-size of ## window-manager decoration (~8 pixels) and some pixels for ## frame/widget borders (~8 widgets x 4 pixels/widget = 32 pixels). set minWinWidthPx [expr {40 + $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 'fRcanvas' 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 may 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(buttonIFACES) "Ifaces" set aRtext(radbuttSMALL) "S" set aRtext(radbuttMEDIUM) "M" set aRtext(radbuttLARGE) "L" ## For '.fRtheme1' frame: set aRtext(labelTHEMEscale) "Theme for the meter :" 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_A23_metallicKnob_indentedBlueGrayEdge_lightGrayBkgd" set aRticsYorN(1) "N" set aRcenterX(1) 0.505 set aRcenterY(1) 0.51 set aRradiusArc(1) 0.35 set aRinORout(1) "out" set aRhexcolorText(1) "#000000" set aRhexcolorTics(1) "#000000" set aRimgPointer(1) "bullet_blueHilited_transp_26x26" set aRimgCornerFeature(1) "screwheadHex_transp_32x32" set aRstartAngle(1) -70 set aRextentAngle(1) 320 ## Theme 2: set aRimgBkgd(2) "meter_background_A10_circularlyPolishedMetal_darkVersion" set aRticsYorN(2) "N" set aRcenterX(2) 0.50 set aRcenterY(2) 0.50 set aRradiusArc(2) 0.35 set aRinORout(2) "in" set aRhexcolorText(2) "#ffffff" set aRhexcolorTics(2) "#ffffff" set aRimgPointer(2) "bullet_greenSolarized_transp_25x25" set aRimgCornerFeature(2) "" set aRstartAngle(2) -44 set aRextentAngle(2) 268 ## Theme 3: set aRimgBkgd(3) "meter_background_A16_graySpeaker_brightMetalEdge_blackShadedBkgd" set aRticsYorN(3) "N" set aRcenterX(3) 0.49 set aRcenterY(3) 0.49 set aRradiusArc(3) 0.30 set aRinORout(3) "in" set aRhexcolorText(3) "#ffffff" set aRhexcolorTics(3) "#ffffff" set aRimgPointer(3) "bullet_orangeGlassy_transp_25x25" set aRimgCornerFeature(3) "screwheadTorx_transp_32x32" set aRstartAngle(3) -70 set aRextentAngle(3) 320 ## Theme 4: set aRimgBkgd(4) "meter_background_A19_lightBlueGlossyDisk_metalEdge_beigeBkgd" set aRticsYorN(4) "N" set aRcenterX(4) 0.50 set aRcenterY(4) 0.50 set aRradiusArc(4) 0.30 set aRinORout(4) "in" set aRhexcolorText(4) "#000000" set aRhexcolorTics(4) "#000000" set aRimgPointer(4) "diamond_blue_transp_25x25" set aRimgCornerFeature(4) "screwhead45degSlot_1_transp_25x25" set aRstartAngle(4) -70 set aRextentAngle(4) 320 ## Theme 5: set aRimgBkgd(5) "meter_background_A21_lightGrayShadedDisk_off-whiteEdge_whiteBkgd" set aRticsYorN(5) "N" set aRcenterX(5) 0.505 set aRcenterY(5) 0.50 set aRradiusArc(5) 0.27 set aRinORout(5) "in" set aRhexcolorText(5) "#000000" set aRhexcolorTics(5) "#000000" set aRimgPointer(5) "bullet_whiteGlossy_transp_25x25" set aRimgCornerFeature(5) "screwhead90degSlottedShinyMetal_transp_24x24" set aRstartAngle(5) -70 set aRextentAngle(5) 320 ## Theme 6: set aRimgBkgd(6) "meter_background_A25_redWhiteBlueShadedDisk_noEdge_whiteBkgd" set aRticsYorN(6) "N" set aRcenterX(6) 0.50 set aRcenterY(6) 0.50 set aRradiusArc(6) 0.35 set aRinORout(6) "in" set aRhexcolorText(6) "#000000" set aRhexcolorTics(6) "#000000" set aRimgPointer(6) "saturn_metallic_transp_25x25" set aRimgCornerFeature(6) "" set aRstartAngle(6) -70 set aRextentAngle(6) 320 ## Theme 7: set aRimgBkgd(7) "meter_background_A30_blackSphere" set aRticsYorN(7) "N" set aRcenterX(7) 0.50 set aRcenterY(7) 0.50 set aRradiusArc(7) 0.35 set aRinORout(7) "in" set aRhexcolorText(7) "#ffffff" set aRhexcolorTics(7) "#ffffff" set aRimgPointer(7) "fluidDrop_red_transp_25x25" set aRimgCornerFeature(7) "" set aRstartAngle(7) -70 set aRextentAngle(7) 320 ## Theme 8: set aRimgBkgd(8) "meter_background_A31_whiteDiskOnDisk_onBlueFabric" set aRticsYorN(8) "N" set aRcenterX(8) 0.50 set aRcenterY(8) 0.50 set aRradiusArc(8) 0.20 set aRinORout(8) "out" set aRhexcolorText(8) "#000000" set aRhexcolorTics(8) "#000000" set aRimgPointer(8) "gear_yellow_transp_25x25" set aRimgCornerFeature(8) "" set aRstartAngle(8) -70 set aRextentAngle(8) 320 ## Theme 9: set aRimgBkgd(9) "meter_background_A32_blueGlossyOrb_blackBkgd" set aRticsYorN(9) "N" set aRcenterX(9) 0.5 set aRcenterY(9) 0.5 set aRradiusArc(9) 0.30 set aRinORout(9) "in" set aRhexcolorText(9) "#ffffff" set aRhexcolorTics(9) "#ffffff" set aRimgPointer(9) "letterX_shinyBlue_transp_25x25" set aRimgCornerFeature(9) "" set aRstartAngle(9) -70 set aRextentAngle(9) 320 ## Theme 10: set aRimgBkgd(10) "meter_background_B06_redShadedDisk_metallicEdge_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) "pacman_yellow_transp_25x25" set aRimgCornerFeature(10) "" 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 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 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 'BUTTON' widgets --- Exit, Help, Refresh, Ifaces --- ## and 3 S/M/L '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} ## Here is the BUTTON for showing installed network interfaces. button .fRbuttons.buttIFACES \ -text "$aRtext(buttonIFACES)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {sho_ifaces} ## Here is the ENTRY field for the network interface-ID. ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set IFACEname "eth0" entry .fRbuttons.entryIFACE \ -textvariable IFACEname \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -width 6 \ -relief sunken \ -bd $BDwidthPx_entry ## 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 '.fRbuttons'. pack .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ .fRbuttons.buttREFRESH \ .fRbuttons.buttIFACES \ .fRbuttons.entryIFACE \ .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 pair ## (for changing the 'refresh rate' for the meter pointers) ## --- 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 5.0 scale .fRsamprate.scaleSECONDS \ -from 0.1 -to 20.0 \ -resolution 0.1 \ -font fontTEMP_SMALL_varwidth \ -variable WAITseconds \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length 200 \ -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 center \ -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 center \ -fill none \ -expand 0 ##+################################################## ## END OF DEFINITION of the GUI widgets. ##+################################################## ## Start of BINDINGS, PROCS, Added-GUI-INIT sections. ##+################################################## ##+################################################################## ##+################################################################## ## BINDINGS SECTION: ##+################################################################## bind .fRbuttons.entryIFACE {Refresh} 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 'entryIFACE' 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_one_meter' proc into a smaller sequence of procs --- or ## adding arguments to the 'make_one_meter' 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 a ## recursive loop, initiated at the bottom of this script. ## 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'. ## ## 'sho_ifaces' - called by the 'Ifaces' button. Shows available ## network interfaces, in a popup message window. ## ## '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: 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 'Pkts/sec' TEXT-LABEL, ## near the top of the meter arc. #################################################################### $canvas create text \ $centerXpx [expr {$centerYpx - (0.40 * $radiusPx)}] \ -anchor center -justify center \ -fill $aRhexcolorText($THEMEindex) \ -text "Pkts/sec" -font { Helvetica 10 normal roman} } ## END OF proc 'make_meters' ##+######################################################################## ## PROC 'update_pointers' ##+######################################################################## ## PURPOSE: Updates the pointers on 2 canvases --- using the ## Linux/Unix/BSD/Mac 'netstat' (or other) command to get ## RX and TX data. ## ## 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 IFACEname \ PREVrx PREVtx PREVclock_millisecs VARsampcnt ########################################################## ## Get RX and TX data --- and the current clock time, ## in millisecs. ########################################################## foreach {CURrx CURtx} \ [exec $DIRthisScript/get_net_stats.sh $IFACEname] {break} set CURclock_millisecs [clock milliseconds] ################################################## ## Increment the sample count, to show on the GUI. ################################################## incr VARsampcnt ## FOR TESTING: if {0} { puts "proc update_pointers:" puts "PREVrx: $PREVrx PREVtx: $PREVtx" puts "PREVclock_millisecs: $PREVclock_millisecs" puts "CURrx: $CURrx CURtx: $CURtx" puts "CURclock_millisecs: $CURclock_millisecs" } ######################################################### ## Calculate the RX and TX rates. ######################################################### set deltaRX [expr {$CURrx - $PREVrx}] set deltaTX [expr {$CURtx - $PREVtx}] set deltaSECS \ [expr {double($CURclock_millisecs - $PREVclock_millisecs)/1000.0}] set RATErx [expr {$deltaRX/$deltaSECS}] set RATEtx [expr {$deltaTX/$deltaSECS}] ######################################################### ## Update the 2 pointers. ######################################################### ## FOR TESTING: (hardcoded meter values) # update_one_pointer .fRcanvases.fRcanvas1 123456789 1234 RX # update_one_pointer .fRcanvases.fRcanvas2 654321 321 TX update_one_pointer .fRcanvases.fRcanvas1 $CURrx $RATErx RX update_one_pointer .fRcanvases.fRcanvas2 $CURtx $RATEtx TX ######################################################### ## Load the current counts to the PREV vars. ######################################################### set PREVrx $CURrx set PREVtx $CURtx set PREVclock_millisecs $CURclock_millisecs ############################################################ ## Force the pointers to show up on the GUI. (Not needed??) ############################################################ # 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 on a specified canvas --- using the canvas ## frame ID and 'tot' and 'rate' numbers passed as arguments. ## TYPE is 'RX' or 'TX'. ## ## CALLED BY: the 'update_pointers' proc ##+######################################################################## proc update_one_pointer {frame TOT RATE TYPE} { ## FOR TESTING: (dummy out this routine) # return global pi radsPERdeg Nsegs MAXrate 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 RATEtext "$TYPE Rate = [format "%0.2f" $RATE] packets/second" set TOTtext "$TOT $TYPE pkts so far this session" $frame.labelINFO1 configure -text "$RATEtext" $frame.labelINFO2 configure -text "$TOTtext" ################################################################### ## Use 'RATE' to locate the pointer around the arc-of-tic-marks. ## To do this: ## Convert RATE 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)}] ######################################################### ## Convert RATE/MAXrate to an angle in radians on the arc. ######################################################### if {$RATE > $MAXrate} {set RATE $MAXrate} set degsPointer [expr {$angle0 - (double($aRextentAngle($THEMEindex)) * $RATE / $MAXrate)}] set radsPointer [expr {$degsPointer * $radsPERdeg}] ## FOR TESTING: # puts "proc 'update_one_pointer' :" # puts "RATE: $RATE MAXrate: $MAXrate 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.40 * $radiusPx*cos($radsPointer)}] set y1 [expr {$centerYpx - 1.40 * $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-time of Rx,Tx 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 sho_ifaces ## ## PURPOSE: Shows available network interfaces, in a ## popup message window. ## ## CALLED BY: 'Ifaces' button ##+############################################################# proc sho_ifaces {} { set IFACES [exec /bin/sh -c {netstat -i | tail -n +3 | awk '{print $1}'}] popup_msgVarWithScroll .topIFACES $IFACES } ## END OF proc 'sho_ifaces' ##+######################################################################## ## 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 'Network Activity' Monitoring Utility ** This utility is meant to show a GUI that holds a pair of tachometer-style meters. The 'pointers' on the meters are updated periodically --- according to a 'Sample rate' (seconds) setting on a 'scale' widget of the GUI --- OR whenever the user chooses to click on the 'Refresh' button. The two meters are to show the RATE of packet receipts and transmissions (RX and TX rates) on a 'network interface' of this computer. *************** DATA COLLECTION: The RX and TX rates (packets per second) are shown on the GUI as numeric-text as well as by the positions of the pointers. This Tcl-Tk script was developed on Linux and uses the 'netstat' command to get the RX and TX data to determine where to relocate the position of the pointers on the meters. An 'Ifaces' button on the GUI can be clicked to get a list of the names of network interfaces known to the 'netstat' command. Typically, the names are 'eth0', 'eth1', or 'wlan0' --- as well as 'lo' for 'local loopback'. Change the interface name in the entry field if you want to monitor a different interface. An integer on the right of the 'Sample rate' scale widget is incremented each time a sample is taken with the 'netstat' command. ************* 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_net_stats.sh' --- that uses the 'netstat' (or other) command to get the RX and TX data --- and extract-and-format the data for return to this Tk script. If the 'netstat' command is not available on your computer, you may have to edit the shell script to use a different command (such as 'ifconfig') to get the RX and TX 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 RX-and-TX values via the 'netstat' ## (or whatever) 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" ##+######################################################## ## Get a default network interface-ID for the entry widget. ##+######################################################## set IFACEname [exec /bin/sh -c {netstat -i | tail -n +3 | awk '{print $1}' | head -1}] # set IFACEname "eth0" # set IFACEname "eth1" # set IFACEname "wlan0" ##+################################################### ## Set the scale widget var for initial 'refresh rate' ## (actually wait-time = 'wave-length', not 'frequency') ## --- in seconds. ##+################################################### # set WAITseconds 5.0 set WAITseconds 2.0 # set WAITseconds 1.0 # set WAITseconds 0.5 ##+##################################################### ## Set a max-rate (packets/sec) to use for the limit ## on the 2 meters. ## ## (This should be compatible with the 'ticLabels' ## max below.) ##+##################################################### set MAXrate 1000 ##+#################################################### ## 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 100 200 300 400 500 600 700 800 900 1000" ##+################################################# ## Draw the 2 tachometers (without pointers). ##+################################################# make_meters ##+#################################################### ## Initialize the variable we use to keep track of ## the sample count. ##+#################################################### set VARsampcnt 0 ##+#################################################### ## Get an initial sample of the RX,TX data, along with ## the time at which the sample was taken --- in ## 'PREV' variables. ##+#################################################### foreach {PREVrx PREVtx} \ [exec $DIRthisScript/get_net_stats.sh $IFACEname] {break} set PREVclock_millisecs [clock milliseconds] ## FOR TESTING: # puts "PREVrx: $PREVrx PREVtx: $PREVtx" # puts "PREVclock_millisecs: $PREVclock_millisecs" ##+###################################################### ## Do an initial draw of the pointers, after WAITseconds. ## ## NOTE: ## 'update_pointers' starts a loop to keep updating the pointers. ## ## The proc 'update_pointers' called itself --- ## with 'after '. ##+###################################################### set WAITmillisecs [expr {int(1000*$WAITseconds)}] after $WAITmillisecs update_pointers