#!/usr/bin/wish -f ##+######################################################################## ## ## SCRIPT: meter_cpus_usage_hidef-image.tk ## ## PURPOSE: This script is meant to show a GUI with a nice-looking, ## high-definition-image METER which shows the PERCENT-ACTIVITY ## of a CPU. ## ## The CPU could be one of several on a computer, or it could ## be the 'total' CPU (which could also be considered to be an ## 'average'), for all the CPU's of the computer. ## ## For a user-specified time interval, the sum of ## 'user', 'nice', 'system', and 'idle' times is found for ## a user-specified CPU. The sum of the first 3 'times' over the ## sum of the four is used to give the PERCENT-ACTIVITY of the CPU. ## ## This utility is designed for computer operating systems of the ## Linux/Unix/BSD type --- computers that have a '/proc/stat' (or ## similar) file that gives the user-nice-system-idle data for each ## cpu. This utility is also designed for computer operating ## systems that have commands like 'grep' and 'awk'. ## ## On computers with a single CPU, the meter simply shows an ## 'activity measure' for that one CPU. ## ## On computers with multiple CPU's, we default to showing ## an 'activity measure' for ALL the CPU's (i.e. a total or ## average for all the CPU's). ## ## Showing a meter simultaneously for each CPU was considered, ## BUT ... ## On systems with multiple CPU's, %-activity meters could be ## shown for all the CPU's, including the total/average CPU --- ## UP TO A LIMIT. The display would require quite a bit of ## screen area for a computer with 8 or more CPU's. Some ## consideration was given to showing a limited subset of ## the CPU's in the case of many CPU's --- but, in the end, ## it seemed better to display info for one CPU at a time --- ## and offer a 'Report' option to show a text-report on ALL ## the CPU's. ## ## We allow the user to specify a particular CPU whose 'activity' ## is to be shown on the meter. ## ## We could have allowed MULTIPLE METERS to be shown --- say 4 --- ## showing %-user, %-nice, %-system, and %-idle --- for ## a single user-specified CPU. But ... ## ## To simplify, we show just ONE METER (for a user-specified ## CPU) --- with the %-user+nice+system (the 'total activity') ## being shown. ## ## When the GUI first appears, the meter will show the %-activity ## for the 'total CPU' (the sum of the CPU's). ## ## Of course, a Tcl-Tk programmer could change this script to ## initially show a single meter for the 'average' CPU --- AND the ## GUI could be implemented to allow the user to show 4 meters, say ## to show individual user,nice,system,idle percents for the ## user-selectable CPU. Then, the user could change from the ## 'average' CPU to any of the individual CPU's. But ... ## ## For relative simplicity of coding, we stay with a single ## meter showing %-user+nice+system --- not multiple meters ## showing %-user, %-nice, %-system, %-idle for a user-selectable ## set of CPU's. ## ################################# ## METHOD OF DRAWING THE METER(S): ## ## 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 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 script uses the Tk canvas 'create image' command to put a ## nice-looking, HD (high-definition) meter background on the canvas. ## 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 'pointer' ## image), placed along the tic-marks arc, to indicate the current %-activity ## for the user-selected CPU. 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. ## ## 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. ## ################### ## THE GUI LAYOUT: ## The options available to the user are compactly indicated ## by the following 'sketch' of the GUI: ## ## --------------------------------------------------------------- ## CPU Activity [window title] ## --------------------------------------------------------------- ## {Exit} {Help} {Report} {Refresh} O Small O Medium O Large ## ## Theme for ## the meter : <--------------------> X DrawTicLabels ## ## X DrawTicsArc X ShowCornerFeatures ## ## {ShowCPUs} Monitor CPU: ________ X Rivets ## ## SampleRate NN ## (seconds) : <----------------------------> ## ## $user = nn.nn %nice = n.nn ## $system = nn.nn %idle = nn.nn ## ## ## The meter will be placed in this area on a Tk canvas. ## ## A background image will be placed here, according to ## the integer setting on the 'theme' scale widget. ## ## 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. ## ---------------------------------------------------------------- ## ## In the GUI diagram above: ## - Braces indicate a Tk 'button' widget. ## - Underscores indicate a Tk 'entry' widget. ## - A colon indicates that the text before the colon is on a 'label' 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 (<-------->) ## is used to indicate a 'scale' widget. ## - Square brackets indicate a comment (not to be placed on the GUI). ## ## NOTE: We may need to abbreviate or move the Small-Medium-Large ## radiobuttons to fit them into the GUI without making ## it too wide. If necessary, we may need to add a line ## below the themes scale widget, for more theme controls ## (checkbuttons and radiobuttons). ## ##+############## ## GUI components: ## ## From the GUI 'sketch' above, it is seen that the GUI consists of ## about ## ## - 5 button widgets ## - 5 label widgets ## - 1 entry widget ## - 3 radiobutton widgets in 1 group ## - 2 checkbutton widgets ## - 2 scale widgets ## - 0 listbox widgets ## ########################################################## ## GETTING & USING CPU-USAGE-DATA for a USER-SPECIFIED CPU: ## ## This script was developed on Linux and uses 'cat /proc/stat' ## to get 'activity' data for individual CPU's and for the ## 'total'/'average' CPU. ## ## A pop-out menu of the available CPU's is provided via a button ## on the GUI. Thus the user can see the ID's of the selectable ## CPU's, whose %-activity can be shown on the meter. ## ## For a user-selected CPU-ID, whenever the 'cat /proc/stat' ## is run, a 'grep' command is used to get the line of data ## for the user-selected CPU-ID. The data is summed and ## divided to get the %-value to be pointed to by the ## 'pointer' of the meter. ## ## The %-user, %-system, %-nice, and %-idle values are also ## shown as text, on label widgets --- just above the meter ## on the canvas. ## ############################################################### ## AUTO-UPDATE of the METER-'POINTER' and TEXT-DATA for the CPU: ## ## The needle (or other style of 'pointer') on the meter is ## updated periodically to show the percent-activity for the ## user-selected CPU. ## ## The amount of time between updates is controlled by a ## scale widget on the GUI --- defaulted to about 5 seconds, ## say. ## ## 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 high ## (say 10 seconds or more) and the user wants to immediately ## reset the sampling rate. (The 'Refresh' button is also ## used to resize the canvas drawing of the meter, after ## the user resizes the GUI window.) ## #################### ## A 'REPORT' OPTION: ## ## The 'cat /proc/stat' command can be used to show all the ## CPU 'ticks' (a.k.a. 'jiffies' - about 1/100th of a second) data, ## for ALL the CPU's of the computer. ## ## This utility uses 'awk' to subtract the data from two ## issues of the 'cat /proc/stat' command, spaced a couple ## of seconds apart. In addition, 'awk' is used to ## format the data into a report with column headings, etc. ## ## The user can click on a 'Report' button on the GUI to ## show the report in a pop-up scrollable Tk text widget. ## The user can copy-paste the text from that window to another ## window such as a text editor window or a word processor window. ## ################################## ## EXAMPLE 'cat /proc/stat' OUTPUT ... from a computer with 2 CPU's : ## ## $ cat /proc/stat ## cpu 183112 1474 25701 1008114 5609 357 1301 0 0 ## cpu0 96100 982 12954 469961 2710 77 88 0 0 ## cpu1 87011 491 12746 538152 2898 280 1213 0 0 ## intr 3118276 1241284 1838 0 0 0 0 0 0 1 17228 0 0 2573 0 0 0 869 62033 0 0 0 0 0 0 0 0 27446 59166 16612 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ## ctxt 4455035 ## btime 1378614163 ## processes 2381 ## procs_running 2 ## procs_blocked 0 ## softirq 1569874 0 1120419 10859 10176 27466 4934 307011 551 88458 ## ## This output is explained via the 'man proc' command. See the section ## that starts '/proc/stat'. ## ## The first 4 numbers on each 'cpu' line are the amount of time, measured ## in units of USER_HZ (approx. 1/100ths of a second on most architectures) ## that the system spent in ## 1) user mode, ## 2) user mode with low priority ('nice' mode), ## 3) system mode, ## 4) the idle task, ## respectively. The other numbers on each 'cpu' line are described below. ## The time is measured from boot time. The output from two consecutive ## queries can be used to get the cpu-activity (user,nice,system,idle) ## over a sample time interval. ## ## In the above example of 2 CPU's (plus the 'total/average CPU'), we use ## the 1st 4 of the 9 columns of data in the 3 lines starting with 'cpu', ## 'cpu0', and 'cpu1'. ## ## The data for 'cpu' is the combined data for 'cpu0' and 'cpu1'. ## The 4th colum is 'idle time' in 'jiffies' (approx. hundredths of ## seconds). Column 4 usually contains most of the 'jiffies'. ## ## The data in the 9 columns is 'jiffies' due to ## ## 1 * user: normal processes executing in user mode ## 2 * nice: niced processes executing in user mode ## 3 * system: processes executing in kernel mode ## 4 * idle: twiddling thumbs ## 5 * iowait: waiting for I/O to complete ## 6 * irq: servicing interrupts ## 7 * softirq: servicing softirqs ## 8 * 'steal_time' - the ticks spent executing other virtual hosts ## (in virtualised environments like Xen) ## 9 * ticks spent on (virtual) guest systems. ## ## We calculate the %-usage of a CPU by calculating a numerator and ## a denominator. For the numerator, we could sum all of the 9 columns ## except column 4 (and 5?). For the denominator we could use the ## sum of all 9 columns. ## ## Rather than using all 9 columns, we follow what others have done ## before and use the 1st 4 columns: user, nice, system, and idle. ## ## The 'pointer' on the meter is updated periodically to show the ## %-activity of the user-specified CPU, where %-activity means ## ## col1+col2+col3 / col1+col2+col3+col4. ## ## Of course, a Tcl-Tk coder could change calculations in this ## utility to use 9, rather than 4, columns of data. ## ##+########### ## METHOD USED to respond to 'Report' and 'ShowCPUs' buttons ## and to update the meter(s): ## ## A Tcl 'exec' command calls on a separate shell script that uses ## the 'cat /proc/stat' command to get the CPU usage data ## and reformat the data for return to this Tk script. ## ## The shell script will be capable of returning info of 3 types: ## 1) activity data for ALL the CPU's - a 'report' ## 2) a list of the CPU ID's ## 3) activity data for a user-specified CPU-ID. ## ##+####################### ## 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. (PNG is better for scaling up or down.) ## ## 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', 'Report', 'Refresh' buttons ## '.fRtheme1' - to contain a scale widget and a checkbutton widget ## '.fRtheme2' - to contain checkbutton widgets ## '.fRcpus' - to contain a 'ShowCPUs' button --- and an entry widget ## '.fRsamprate' - to contain a scale widget, with a label widget. ## '.fRmeters' - to contain 2 label widgets over a square canvas ## widget, which will display the meter. ## ## Sub-frames: ## '.fRmeters.fRmeter1' - for 2 (or more) label widgets & 1 canvas widget, ## corresponding to one CPU-ID ## ## (The '.fRmeters' frame could contain more sub-frames ## someday, to hold more meters shown simultaneously.) ## ## 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 up to one (or more) meter(s) within ## Tk canvas(es). ## ## 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 one or more 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'. ## ## 'Report' - called by 'Report' button. ## ## 'show_cpus' - called by 'ShowCPUs' button. ## ## 'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var. ## Also used by the 'Report' button and the ## 'ShowCPUs' button. ## ## 5) Additional GUI Initialization: ## - call 'make_meters' to put the meter(s) in the canvas(es). ## - call 'update_pointers' --- to initialize the pointer location(s). ## ##+####################################################################### ## 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 2013dec18 Started the basic code of the ## script based on my previous ## meters scripts, which use the ## 'shadow-circle' technique of ## a demo script by Marco Maggi ## at http://wiki.tcl.tk/9107 ## Changed by: Blaise Montandon 2015oct30 Changed pack of canvas to 'center' ## instead of 'nw'. Fixed filenames ## of 'pointers' and 'corner_features'. ## Changed by: Blaise Montandon 2015nov11 Finalized set of 10 themes and ## their positioning parameters. ## Changed some DIR variable names. ##+######################################################################## ##+###################################################### ## Set WINDOW TITLE and POSITION. ##+###################################################### wm title . "CPU Activity" wm iconname . "CPU use" wm geometry . +15+30 ##+###################################################### ## Set the COLOR SCHEME for the window and its widgets --- ## such as listbox and entry field background color. ##+###################################################### tk_setPalette "#e0e0e0" set entryBKGD "#ffffff" # set listboxBKGD "#ffffff" set radbuttBKGD "#ffffff" set chkbuttBKGD "#ffffff" set scaleBKGD "#f0f0f0" set textBKGD "#f0f0f0" ##+######################################################## ## 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 text in ENTRY fields --- and often for text in ## TEXT widgets. ##+######################################################## 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) ##+########################################################### ## CANVAS widget geom settings: set initCanWidthPx 200 set initCanHeightPx 200 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 ## LABEL widget geom settings: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 ## BUTTON widget geom settings: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 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 ##+###################################################################### ## Set a MIN-SIZE of the window (roughly). ## ## For WIDTH, allow for the min-width of the '.fRbuttons' frame ## --- the several button widgets in the 'fRbuttons' frame. ## ## For HEIGHT, allow for the stacked frames: ## 1 char high for the '.fRbuttons' frame, ## 2 chars high for the '.fRtheme1' frame, ## 1 char high for the '.fRtheme2' frame, ## 1 char high for the '.fRcpus' frame, ## 2 chars high for the '.fRsamprate' frame, ## at least 50 pixels high for the '.fRmeters' frame. ##+##################################################################### ## FOR WIDTH: set minWidthPx [font measure fontTEMP_varwidth \ " Exit Help Report Refresh "] ## We add some pixels to account for right-left-size of ## window-manager decoration (~8 pixels) and some pixels for ## frame/widget borders (~4 widgets x 4 pixels/widget = 16 pixels). set minWinWidthPx [expr {24 + $minWidthPx}] ## For HEIGHT --- for ## 1 char high for 'fRbuttons' ## 2 chars high for 'fRtheme1' ## 1 char high for 'fRtheme2' ## 1 char high for 'fRcpus' ## 2 chars high for 'fRsamprate' ## ~50 pixels high for 'fRmeters' set charHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {7 * $charHeightPx}] ## Add about 50 pixels for height of the canvas ## AND add about 20 pixels for top-bottom window decoration -- ## and some pixels for top-and-bottom of frame/widget borders ## (~6 widgets x 4 pixels/widget = 24 pixels). set minWinHeightPx [expr {94 + $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 the user ## choosing Small/Medium/Large meter backgrounds. ## 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(buttonREPORT) "Report" set aRtext(buttonREFRESH) "Refresh" set aRtext(radbuttSMALL) "S" set aRtext(radbuttMEDIUM) "M" set aRtext(radbuttLARGE) "L" ## For '.fRtheme' 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 '.fRcpus' frame: set aRtext(buttonCPUS) "ShowCPUs" set aRtext(labelCPU) " MonitorCPU:" ## For '.fRsamprate' frame: set aRtext(labelSAMPLEscale) "SampleRate (seconds) :" ## END OF if { "$VARlocale" == "en"} ##+############################################################## ## DEFINE THE AVAILABLE THEMES, VIA SEVERAL ARRAYS. ## Recall, from above: ## 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. ##+############################################################## ## Theme 1: set aRimgBkgd(1) "meter_background_A01_3DsphericalPuzzle-whiteBkgd" set aRticsYorN(1) "N" set aRcenterX(1) 0.50 set aRcenterY(1) 0.50 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) "screwheadHorizSlot_1_transp_24x24" set aRstartAngle(1) -70 set aRextentAngle(1) 320 ## Theme 2: set aRimgBkgd(2) "meter_background_A02_blackShadedDisk_metallicEdge_blueTOredARC_whiteBkgd" set aRticsYorN(2) "N" set aRcenterX(2) 0.50 set aRcenterY(2) 0.49 set aRradiusArc(2) 0.30 set aRinORout(2) "in" set aRhexcolorText(2) "#ffffff" set aRhexcolorTics(2) "#ffffff" set aRimgPointer(2) "bullet_orangeGlassy_transp_25x25" set aRimgCornerFeature(2) "" set aRstartAngle(2) -43 set aRextentAngle(2) 266 ## Theme 3: set aRimgBkgd(3) "meter_background_A51_coin_washington-quarter_1980_InGoodWeTrust" set aRticsYorN(3) "N" set aRcenterX(3) 0.50 set aRcenterY(3) 0.50 set aRradiusArc(3) 0.35 set aRinORout(3) "in" set aRhexcolorText(3) "#000000" set aRhexcolorTics(3) "#000000" set aRimgPointer(3) "bullet_8ball_transp_25x25" set aRimgCornerFeature(3) "screwheadPhillips_1_transp_26x26" set aRstartAngle(3) -70 set aRextentAngle(3) 320 ## Theme 4: set aRimgBkgd(4) "meter_background_A17_greenLeaf_closeup" set aRticsYorN(4) "N" set aRcenterX(4) 0.50 set aRcenterY(4) 0.50 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) "rivetRoundHead_transp_25x25" set aRstartAngle(4) -70 set aRextentAngle(4) 320 ## Theme 5: set aRimgBkgd(5) "meter_background_B04_blackSolidOval_metallicEdge_ABSTRACT1" set aRticsYorN(5) "N" set aRcenterX(5) 0.50 set aRcenterY(5) 0.50 set aRradiusArc(5) 0.25 set aRinORout(5) "in" set aRhexcolorText(5) "#ffffff" set aRhexcolorTics(5) "#ffffff" set aRimgPointer(5) "bullet_8ball_transp_25x25" set aRimgCornerFeature(5) "screwheadPhillips_4_transp_18x18" set aRstartAngle(5) -70 set aRextentAngle(5) 320 ## Theme 6: set aRimgBkgd(6) "meter_background_A05_blackSolidDisk_metallicEdge_grayBkgd_WATCH8screws" set aRticsYorN(6) "N" set aRcenterX(6) 0.50 set aRcenterY(6) 0.50 set aRradiusArc(6) 0.30 set aRinORout(6) "in" set aRhexcolorText(6) "#ffffff" set aRhexcolorTics(6) "#ffffff" set aRimgPointer(6) "bullet_whiteGlossy_transp_25x25" set aRimgCornerFeature(6) "" set aRstartAngle(6) -70 set aRextentAngle(6) 320 ## Theme 7: set aRimgBkgd(7) "meter_background_A03_blackSolidDisk_grayTOgreenBkgd_BULLEThole" set aRticsYorN(7) "N" set aRcenterX(7) 0.50 set aRcenterY(7) 0.50 set aRradiusArc(7) 0.19 set aRinORout(7) "in" set aRhexcolorText(7) "#ffffff" set aRhexcolorTics(7) "#ffffff" set aRimgPointer(7) "bullet_greenSolarized_transp_25x25" set aRimgCornerFeature(7) "rivetRoundHead_transp_25x25" set aRstartAngle(7) -70 set aRextentAngle(7) 320 ## Theme 8: set aRimgBkgd(8) "meter_background_A04_blackSolidDisk_greenBkgd_BULLEThole" 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) "#ffffff" set aRhexcolorTics(8) "#ffffff" set aRimgPointer(8) "bullet_greenSolarized_transp_25x25" set aRimgCornerFeature(8) "rivetRoundHead_transp_25x25" set aRstartAngle(8) -70 set aRextentAngle(8) 320 ## Theme 9: set aRimgBkgd(9) "meter_background_A15_glossyRedBall_blackHilitedHood_noBkgd" set aRticsYorN(9) "N" set aRcenterX(9) 0.50 set aRcenterY(9) 0.50 set aRradiusArc(9) 0.35 set aRinORout(9) "in" set aRhexcolorText(9) "#ffffff" set aRhexcolorTics(9) "#ffffff" set aRimgPointer(9) "bullet_redHilited_transp_25x25" set aRimgCornerFeature(9) "screwheadPhillips_shinyMetal_transp_26x26" set aRstartAngle(9) -70 set aRextentAngle(9) 320 ## Theme 10: set aRimgBkgd(10) "meter_background_A18_grinningPumpkin_noEdge_blackBkgd" set aRticsYorN(10) "N" set aRcenterX(10) 0.466 set aRcenterY(10) 0.48 set aRradiusArc(10) 0.30 set aRinORout(10) "in" set aRhexcolorText(10) "#000000" set aRhexcolorTics(10) "#000000" set aRimgPointer(10) "bullet_lightbulb_transp_25x25" set aRimgCornerFeature(10) "screwheadTorx_transp_26x26" set aRstartAngle(10) -70 set aRextentAngle(10) 320 ## This 'Nthemes' variable will be used to define the 'samprate' scale. set Nthemes 10 ##+################################################################ ## DEFINE-AND-PACK *ALL* THE FRAMES: ## ## Top-level : '.fRbuttons' , '.fRtheme', ## '.fRcpus', '.fRsamprate', '.fRmeters' ## ## Sub-frames: '.fRmeters.fRmeter1' one meter, for now. ##+################################################################ ## FOR TESTING: (to see size of frames as window is resized) # set BDwidth_frame 2 # set RELIEF_frame raised 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 .fRcpus -relief $RELIEF_frame -bd $BDwidth_frame frame .fRcpus -relief raised -bd 2 frame .fRsamprate -relief $RELIEF_frame -bd $BDwidth_frame frame .fRmeters -relief $RELIEF_frame -bd $BDwidth_frame ##+########################################################## ## SUB-FRAME DEFINITIONS: ##+########################################################## set MAXmeters 1 # set MAXmeters 4 frame .fRmeters.fRmeter1 -relief raised -bd 2 # frame .fRmeters.fRmeter2 -relief raised -bd 2 # frame .fRmeters.fRmeter3 -relief raised -bd 2 # frame .fRmeters.fRmeter4 -relief raised -bd 2 ##+############################## ## PACK the top-level 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 .fRcpus \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRsamprate \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRmeters \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+################################################### ## We pack a single canvas sub-frame (meter), for now. ## ## Someday we may allow for at least two meters. ##+################################################### pack .fRmeters.fRmeter1 \ -side left \ -anchor nw \ -fill both \ -expand 1 ##+########################################################## ## The FRAMES ARE PACKED. START PACKING WIDGETS IN THE FRAMES. ##+########################################################## ##+########################################################## ## In FRAME '.fRbuttons' - ## DEFINE-and-PACK 'BUTTON' WIDGETS ## --- Exit, Help, Refresh, Report BUTTONS. ##+########################################################## 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.buttREPORT \ -text "$aRtext(buttonREPORT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {Report} 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 ## Pack the widgets in frame '.fRbuttons'. pack .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ .fRbuttons.buttREPORT \ .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 '.fRcpus' - ## DEFINE-and-PACK a 'ShowCPUs' BUTTON, and ## a CPU-ID ENTRY widget. ##+########################################################## ## Here is the BUTTON for showing a list of CPU IDs. button .fRcpus.buttCPUS \ -text "$aRtext(buttonCPUS)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {show_cpus} label .fRcpus.labelCPU \ -text "$aRtext(labelCPU)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Here is the ENTRY field for a CPU name. ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set CPUname "cpu" entry .fRcpus.entryCPU \ -textvariable CPUname \ -bg $entryBKGD \ -font fontTEMP_fixedwidth \ -width 9 \ -relief sunken \ -bd $BDwidthPx_entry ## Pack the widgets in frame '.fRcpus'. pack .fRcpus.buttCPUS \ .fRcpus.labelCPU \ .fRcpus.entryCPU \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRsamprate' - ## DEFINE-and-PACK a LABEL-AND-SCALE widget pair --- ## for changing the 'refresh rate' for the meter pointer(s). ##+########################################################## ## Here is the LABEL-AND-SCALE pair for the wait-seconds (sample rate). 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 180 \ -width $scaleThicknessPx ## Here is a label to show the current sample count. label .fRsamprate.labelCOUNT \ -textvariable VARsampcnt \ -font fontTEMP_varwidth \ -justify right \ -anchor w \ -relief flat \ -bd 0 ## Pack the widgets in frame '.fRsamprate'. pack .fRsamprate.labelSAMPLEscale \ .fRsamprate.scaleSECONDS \ .fRsamprate.labelCOUNT \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRmeters.fRmeter1' - ## 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 .fRmeters.fRmeter1.labelINFO1 \ -text "" \ -font fontTEMP_SMALL_fixedwidth \ -justify left \ -anchor w \ -relief raised \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label label .fRmeters.fRmeter1.labelINFO2 \ -text "" \ -font fontTEMP_SMALL_fixedwidth \ -justify left \ -anchor w \ -relief raised \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label canvas .fRmeters.fRmeter1.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 ## Pack the widgets in frame '.fRmeters.fRmeter1'. pack .fRmeters.fRmeter1.labelINFO1 \ .fRmeters.fRmeter1.labelINFO2 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRmeters.fRmeter1.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 .fRtheme1.scaleTHEME {Refresh} bind .fRbuttons.radbuttSMALL {Refresh} bind .fRbuttons.radbuttMEDIUM {Refresh} bind .fRbuttons.radbuttLARGE {Refresh} bind .fRcpus.entryCPU {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: ## We assume we are 'drawing' one meter per CPU. ## Although we draw only one meter on the GUI in this implementation, ## we structure the procs so that they could, with relatively little ## change, support drawing more than one meter on the GUI per CPU --- ## OR meters on the GUI for more than one CPU. ## ## 'make_meters' - to draw meter(s) within Tk canvas(es). ## ## 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' - to draw one meter. Called by 'make_meters' ## to make the meter(s). ## ## Calls on the 'get_cpu_usage_info.sh' script ## with the CPU-ID for its single argument. ## ## 'update_pointers' - to update the 'pointer(s)' on the meter(s). ## Called initially at the bottom of this script. ## And called in the 'Refresh' proc. ## ## 'update_one_pointer' - to place one 'pointer' on a specified meter/canvas. ## Called by 'update_pointers' to update each of the ## pointers on the one or more meters. ## ## 'Refresh' - called by the 'Refresh' button. Runs the procs ## 'make_meters' and 'update_pointers' --- in particular, ## for the user to force the meters to be resized if ## the user resizes the meter with the S-M-L radiobuttons ## --- and whenever the user wants a new start of the ## sampling, typically on changing the sample rate. ## ## 'Report' - called by the 'Report' button. This Report proc ## calls on the 'get_cpu_usage_info.sh' script ## with the 'all' parameter. ## ## 'show_cpus' - called by the 'ShowCPUs' button. This proc ## calls on the 'get_cpu_usage_info.sh' script ## with the 'cpuslist' parameter. ## ## 'popup_msgVarWithScroll' - to show the HELPtext var. Called by the 'Help' button. ## Also used by the 'Report' and 'ShowCPUs' procs. ## ##+################################################################# ##+######################################################################## ## PROC 'make_meters' ##+######################################################################## ## PURPOSE: Draws all features (except the 'pointers') of one or more ## high-definition meters. ## ## The size of the meter 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. ##+######################################################################## # set Nmeters 1 proc make_meters {} { # global Nmeters ## FOR TESTING: (to dummy out this proc) # return ############################################################ ## Get current '.fRmeters' dimensions --- in case the user ## has resized the window, and thus the '.fRmeters' frame, ## which was packed with '-fill both -expand 1'. ############################################################ # set curMetersFrameWidthPx [winfo width .fRmeters] # set curMetersFrameHeightPx [winfo height .fRmeters] ############################################################ ## Adjust height for 2 text lines in the '.fRmeters' frame ## for %-user and %-system processing activity for the ## user-specified CPU. ############################################################ # set charHeightPx [font metrics fontTEMP_SMALL_fixedwidth -linespace] # set curMetersFrameHeightPx \ # [expr {$curMetersFrameHeightPx - (2 * $charHeightPx)}] ######################################################### ## Draw meter1 (without pointer). ######################################################### make_one_meter .fRmeters.fRmeter1.can ######################################################### ## If it is ever decided to draw more meters (such ## as additional meters for 'user' and 'system' and 'idle' ## percents, as well as a meter for 'total' percent ## activity), we could call on 'make_one_meter' additional ## times here. ######################################################### # make_one_meter .fRmeters.fRmeter2.can # make_one_meter .fRmeters.fRmeter3.can # make_one_meter .fRmeters.fRmeter4.can ## OR, use a loop such as: # for {set i 1} {$i < $Nmeters} {incr i} { # make_one_meter $i # } ####################################################### ## NEEDED(?) to force the canvases and frames to update ## according to the new meter (image-on-canvas) sizes. ####################################################### update } ## 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: ## - the meter background image - with canvas 'create image' ## - tic-marks following an arc path - with canvas 'create line' ## - optionally (according to a checkbutton setting), an arc ## along the tic-marks - with canvas 'create arc' ## - optionally (according to a checkbutton setting), labels ## for the tic-marks - with canvas 'create text' ## - 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. ## ## A CPU-ID is passed into this proc to .... ## ## 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} { 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 ## NOTE: centerXpx centerYpx radiusPx are calculated in this proc, ## and they are made global variables for use in the ## 'update_one_needle' proc, so that proc does not have ## to do the calculations again. We prefer that those ## calculations only be done when the meter image is changed. ## FOR TESTING: (to dummy out this proc) # return ################################################################ ## 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 ACCORDINT TO THE NEW METER IMAGE. ################################################################## $canvas configure -width $meterWidthPx $canvas configure -height $meterHeightPx ## Wait and do the update for all the meters in the ## 'make_meters' proc? # update #################################################################### ## SET THE CORNER COORDS FOR DETERMINING THE ARC PATH (for tic-marks ## and optional labels and optional arc). ## We use the 'center point' and 'radius' ratio-parameters of ## the chosen theme --- from arrays aRcenterX aRcenterY aRradiusArc, ## at theme index, $THEMEindex. #################################################################### set topleftXpx \ [expr {($aRcenterX($THEMEindex) * $meterWidthPx) - \ ($aRradiusArc($THEMEindex) * $meterMinPx)}] set topleftYpx \ [expr {($aRcenterY($THEMEindex) * $meterHeightPx) - \ ($aRradiusArc($THEMEindex) * $meterMinPx)}] set botrightXpx \ [expr {($aRcenterX($THEMEindex) * $meterWidthPx) - \ ($aRradiusArc($THEMEindex) * $meterMinPx)}] set botrightYpx \ [expr {($aRcenterY($THEMEindex) * $meterHeightPx) - \ ($aRradiusArc($THEMEindex) * $meterMinPx)}] ########################################################### ## SET parameters for drawing TIC-MARKS and/or LABELS ## around the ARC. ########################################################### 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 .fRmeters.fRmeter1.can create image $x1 $y1 \ -anchor nw -image imgID_feature ## Place upper-right feature. set x1 [expr {$meterWidthPx - $FEATUREoffsetPx}] set y1 $FEATUREoffsetPx .fRmeters.fRmeter1.can create image $x1 $y1 \ -anchor ne -image imgID_feature ## Place lower-left feature. set x1 $FEATUREoffsetPx set y1 [expr {$meterHeightPx - $FEATUREoffsetPx}] .fRmeters.fRmeter1.can create image $x1 $y1 \ -anchor sw -image imgID_feature ## Place lower-right feature. set x1 [expr {$meterWidthPx - $FEATUREoffsetPx}] set y1 [expr {$meterHeightPx - $FEATUREoffsetPx}] .fRmeters.fRmeter1.can 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.30 * $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 pointer(s) on the canvas(es) --- using the ## Linux '/proc/stat' file to get 'activity' data ## for the current user-selected CPU. ## ## This proc uses the 'update_one_pointer' proc to update ## the pointer for each of the Nmeters canvases. ## ## CALLED BY: the 'Additional GUI Initialization' section at the ## bottom of this script, and within this proc itself. ## Also within the 'Refresh' proc. ##+######################################################################## proc update_pointers {} { global argv0 DIRthisScript WAITseconds VARsampcnt CPUname \ PREVuser PREVnice PREVsystem PREVidle # global env ########################################################## ## Get user-nice-system-idle-etc data --- for$CPUname. ########################################################## foreach {CPUcheck CURuser CURnice CURsystem CURidle} \ [exec $DIRthisScript/get_cpu_usage_info.sh $CPUname] {break} set PREVtot [expr {$PREVuser + $PREVnice + $PREVsystem + $PREVidle}] set CURtot [expr {$CURuser + $CURnice + $CURsystem + $CURidle}] incr VARsampcnt ## FOR TESTING: if {0} { puts "proc update_pointers:" puts "CPUname: $CPUname" puts "PREVuser: $PREVuser CURuser: $CURuser" puts "PREVnice: $PREVnice CURnice: $CURnice" puts "PREVsystem: $PREVsystem CURsystem: $CURsystem " puts "PREVidle: $PREVidle CURidle: $CURidle" } ############################################################## ## Update the pointer for $CPUname with %-user+nice+system data. ############################################################## set denom [expr {$CURtot - $PREVtot}] if {$denom <= 0} { set PERCENTtot 0.0 set PERCENTuser 0.0 set PERCENTnice 0.0 set PERCENTsystem 0.0 set PERCENTidle 0.0 } else { set numer8or [expr {($CURuser + $CURnice + $CURsystem) - \ ($PREVuser + $PREVnice + $PREVsystem)}] set PERCENTtot [expr {100.0 * $numer8or / $denom}] set PERCENTuser [expr {100.0 * ($CURuser - $PREVuser) / $denom}] set PERCENTnice [expr {100.0 * ($CURnice - $PREVnice) / $denom}] set PERCENTsystem [expr {100.0 * ($CURsystem - $PREVsystem) / $denom}] set PERCENTidle [expr {100.0 * ($CURidle - $PREVidle) / $denom}] } update_one_pointer .fRmeters.fRmeter1 $CPUname $PERCENTtot \ $PERCENTuser $PERCENTnice $PERCENTsystem $PERCENTidle ############################################################ ## Force the pointers to show up on the GUI. (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. ################################################################ set WAITmillisecs [expr {int($WAITseconds * 1000)}] after $WAITmillisecs update_pointers ################################################################ ## Reset the PREV vars. ################################################################ set PREVuser $CURuser set PREVnice $CURnice set PREVsystem $CURsystem set PREVidle $CURidle } ## END OF proc 'update_pointers' ##+######################################################################## ## PROC 'update_one_pointer' ##+######################################################################## ## PURPOSE: Updates a pointer on a square canvas --- using the ## canvas ID and the %-activity data passed as arguments. ## ## Input is the canvas ID. This proc queries the canvas to ## get its center and to determine an appropriate length for ## the pointer as a proportion of the (square) canvas size. ## ## CALLED BY: the 'update_pointers' proc ##+######################################################################## proc update_one_pointer \ {frame CPUNAME PERCENT PERCENTuser PERCENTnice PERCENTsystem PERCENTidle} { ## For the meter background image, we calculated the ## 'center point' and the radius of the arc (for the current theme ## and SML size) in the 'make_one_meter' proc. Those results were ## put in global vars: centerXpx centerYpx radiusPx ## ## (This works OK because we are using only one meter for this version. ## This is one way to avoid recalculating those 3 variables.) global pi radsPERdeg Nsegs centerXpx centerYpx radiusPx \ aRextentAngle aRstartAngle \ THEMEindex aRimgPointer aRinORout aRhexcolorText ## FOR TESTING: (dummy out this routine) # return ################################################# ## Put data in 2 label lines above the meter. ################################################# $frame.labelINFO1 configure -text \ "%user = [format "%6.2f" $PERCENTuser] %nice = [format "%6.2f" $PERCENTnice]" $frame.labelINFO2 configure -text \ "%system = [format "%6.2f" $PERCENTsystem] %idle = [format "%6.2f" $PERCENTidle]" #################################################################### ## Before drawing the pointer, ## draw a text-label, CPUNAME, near the bottom of the meter arc. #################################################################### catch {$frame.can delete -tags TAG_cputext} $frame.can create text \ $centerXpx [expr {$centerYpx + (0.20 * $radiusPx)}] \ -anchor center -justify center \ -fill $aRhexcolorText($THEMEindex) \ -text "CPU: $CPUNAME" -font { Helvetica 10 normal roman} \ -tag TAG_cputext ############################################################ ## Prepare to draw the pointer on the meter. ############################################################ ## SET THE ANGLE FOR THE ZERO-POINT on the arc-of-tic-marks. ############################################################ set angle0 [expr {$aRextentAngle($THEMEindex) + $aRstartAngle($THEMEindex)}] ########################################################## ## Convert $PERCENT to an angle in radians on the arc. ########################################################## if {$PERCENT > 100} {set PERCENT 100} set degs [expr {$angle0 - (double($aRextentAngle($THEMEindex)) * $PERCENT / 100.0)}] set rads [expr {$degs * $radsPERdeg}] ## FOR TESTING: # puts "proc 'update_one_pointer' :" # puts "PERCENT: $PERCENT degs: $degs rads: $rads" ##################################### ## 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($rads)}] set y1 [expr {$centerYpx - 0.60 * $radiusPx*sin($rads)}] } else { set x1 [expr {$centerXpx + 1.35 * $radiusPx*cos($rads)}] set y1 [expr {$centerYpx - 1.35 * $radiusPx*sin($rads)}] } $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($rads)}] set ytip [expr {$centerYpx - 0.60 * $radiusPx*sin($rads)}] 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 meter(s) and their pointers --- ## for when the user wants a new 'reading' for ## the user-specified CPU ## and/or after the user resizes the window. ## ## CALLED BY: 'Refresh' button ##+############################################################# proc Refresh {} { global DIRthisScript CPUname PREVuser PREVnice PREVsystem PREVidle ## 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 } foreach {CPUcheck PREVuser PREVnice PREVsystem PREVidle} \ [exec $DIRthisScript/get_cpu_usage_info.sh $CPUname] {break} ## FOR TESTING: if {0} { puts "proc Refresh:" puts "CPUname: $CPUname" puts "PREVuser: $PREVuser" puts "PREVnice: $PREVnice" puts "PREVsystem: $PREVsystem" puts "PREVidle: $PREVidle" } ## Wait about 0.5 or 1.0 secs to get the next sample ## to update the pointer(s) on the meter(s). after 500 # after 1000 make_meters update_pointers } ## END OF proc 'Refresh' ##+############################################################# ## proc show_cpus ## ## PURPOSE: Shows available CPU IDs --- in a ## popup message window. ## ## CALLED BY: 'ShowCPUs' button ##+############################################################# proc show_cpus {} { global DIRthisScript set CPUSlist [exec $DIRthisScript/get_cpu_usage_info.sh cpuslist] popup_msgVarWithScroll .topIFACES $CPUSlist } ## END OF proc 'show_cpus' ##+############################################################# ## proc Report ## ## PURPOSE: Shows a report on CPU data for ALL the CPUs, ## in a popup message window. ## ## CALLED BY: 'Report' button ##+############################################################# proc Report {} { global DIRthisScript ############################################################ ## Get the report from an external shell script, into a ## Tcl variable. Then show the report in a popup window. ## ## The shell script gets data for ALL the CPUs, then, ## after about 0.5 or 1 second, it gets the data for ## ALL the CPUs, again. The script combines and sorts ## the two lists of data for the CPU-IDs and feeds the ## sorted list into an 'awk' program to build the report. ########################################################### set REPORTtext [exec $DIRthisScript/get_cpu_usage_info.sh all] popup_msgVarWithScroll .topREPORT "$REPORTtext" } ## END OF proc 'Report' ##+############################################################# ## proc ReDraw_if_canvases_resized ## ## PURPOSE: To handle resizing the meter(s) when the window is ## resized --- IF the binding is implemented. ## ## The intent is to avoid too many redraws --- for ## almost every little resize of the window as its ## border(s) are dragged. ## ## CALLED BY: bind .fRmeters.can ## at bottom of this script. ##+############################################################# ## NOT IMPLEMENTED. ## Code is included for possible future development. ##+############################################################# proc ReDraw_if_canvases_resized {} { global PREVcanvasesWidthPx PREVcanvasesHeightPx draw_wait0or1 ## FOR TESTING: (to dummy out this proc) # return if {$draw_wait0or1 == 1} {return} ## Set the wait indicator and delay doing the canvas resize ## check for about 300 milliseconds --- to allow time for the ## user to stop moving the window. After about 300 milliseconds, ## it is unlikely that the window is moving and thus causing ## multiple redraws. set draw_wait0or1 1 after 900 set CURcanvasesWidthPx [winfo width .fRmeters] set CURcanvasesHeightPx [winfo height .fRmeters] if { $CURcanvasesWidthPx != $PREVcanvasesWidthPx || \ $CURcanvasesHeightPx != $PREVcanvasesHeightPx} { ## The 'after' could be used to prevent too many ## redraws (and flickering and unnecessary processing) ## as the window is being moved. # after 200 make_meters update_pointers set PREVcanvasesWidthPx $CURcanvasesWidthPx set PREVcanvasesHeightPx $CURcanvasesHeightPx set draw_wait0or1 0 } } ## END OF proc 'ReDraw_if_canvases_resized' ##+######################################################################## ## PROC 'popup_msgVarWithScroll' ##+######################################################################## ## PURPOSE: Report help or error conditions to the user. ## ## We do not use focus,grab,tkwait in this proc, ## because we use it to show help when the GUI is idle, ## and we may want the user to be able to keep the Help ## window open while doing some other things with the GUI ## such as putting a filename in the filename entry field ## or clicking on a radiobutton. ## ## For a similar proc with focus-grab-tkwait added, ## see the proc 'popup_msgVarWithScroll_wait' in a ## 3DterrainGeneratorExaminer Tk script. ## ## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk', ## 4th edition, by Welch, Jones, Hobbs. ## ## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg) ## and a variable holding text (many lines, if needed). ## ## CALLED BY: 'help' button ##+######################################################################## ## To have more control over the formatting of the message (esp. ## words per line), we use this 'toplevel-text' method, ## rather than the 'tk_dialog' method -- like on page 574 of the book ## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications ## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor". ##+######################################################################## proc popup_msgVarWithScroll { toplevName VARtext } { ## 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_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" scrollbar $toplevName.scrolly \ -orient vertical \ -command "$toplevName.text yview" scrollbar $toplevName.scrollx \ -orient horizontal \ -command "$toplevName.text xview" } else { text $toplevName.text \ -wrap none \ -font fontTEMP_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 } button $toplevName.butt \ -text "OK" \ -font fontTEMP_varwidth \ -command "destroy $toplevName" ############################################### ## PACK *ALL* the widgets in frame '$toplevName'. ############################################### ## Pack the bottom button BEFORE the ## bottom x-scrollbar widget, pack $toplevName.butt \ -side bottom \ -anchor center \ -fill none \ -expand 0 if {$VARheight > 10} { ## Pack the scrollbars BEFORE the text widget, ## so that the text does not monopolize the space. pack $toplevName.scrolly \ -side right \ -anchor center \ -fill y \ -expand 0 ## DO NOT USE '-expand 1' HERE on the Y-scrollbar. ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA. pack $toplevName.scrollx \ -side bottom \ -anchor center \ -fill x \ -expand 0 ## DO NOT USE '-expand 1' HERE on the X-scrollbar. ## THAT KEEPS THE TEXT AREA FROM EXPANDING. pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } else { pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## $toplevName.text delete 1.0 end $toplevName.text insert end $VARtext $toplevName.text configure -state disabled } ## END OF PROC 'popup_msgVarWithScroll' ##+######################## ## END of PROC definitions. ##+######################## ## Set HELPtext var. ##+######################## set HELPtext "\ \ \ ** HELP for this 'CPU Activity' Monitoring Utility ** This utility is meant to show a GUI that holds a high-definition METER, with a range of 0 to 100 percent. The pointer on the meter shows the PERCENT of usage of a CPU --- for a user-specifiable CPU name (IDentifier), where the computer running this utility may have multiple CPU's. Initially, the 'CPU name' is set to 'cpu' which represents the total (or average) activity of ALL the CPU's for a computer with multiple CPU's. If the computer has a single CPU, then the CPU name 'cpu' refers to that single CPU. The 'ShowCPUs' button can be used to show all the CPU ID's available on the computer. Example ID's for a 2-CPU computer: 'cpu', 'cpu0', and 'cpu1'. For example, if 'cpu0' is a valid CPU ID, that CPU name can be entered in the 'MonitorCPU' entry field to reset the CPU for which the meter displays the PERCENT-USAGE. The 'pointer' on the meter (which is typically supplied as a small glossy ball image) is updated periodically --- according to a 'SampleRate' (seconds) setting on the GUI. The seconds-setting can be reset by a 'scale' widget. Alternatively, instead of depending on auto-updates, the user may choose to click on a 'Refresh' button --- to immediately update the meter with a new PERCENT-USAGE value for the user-selected CPU. Then auto-updates continue according to the current 'SampleRate' setting. Besides PERCENT-USAGE, the actual '%-user' and '%-system' values may be shown for the user-selected CPU. The user can click on a 'Report' button on the GUI to show the activity values, in detail, for ALL the CPU's of the computer. The report is shown in a popup text window. The user can copy-and-paste the text into another window, such as a text editor or word processor window. This Tcl-Tk script was developed on Linux and uses the '/proc/stat' file to get the CPU acitivity data --- for the meter and for the 'Report' option and the 'ShowCPUs' option. *************************************** THEMES (for the meter background, etc.): A 'scale' widget on the GUI can be used to select from a set of themes. The themes are referenced by an integer index and include - an image for the meter background - an image for the 'pointer' that moves around an arc on the meter background - an optional image for a feature to be placed at the four corners of the meter background - X and Y locations for the center of the arc to be used to determine the path of the 'pointer' on the meter - a radius for that arc - an indicator whether the tic-marks and labels should be drawn to the outside OR the inside of the arc - an indicator whether tic marks should be drawn (the tic marks could be supplied as part of the meter background image) - a color for the ticmarks - a color for the ticmark text labels There are several themes supplied with this GUI. But the user can add their own meter-background images and 'pointer' images and related theme-parameters, by updating a set of theme-parameter arrays ---in a section of the code near the arrays for setting the text that appears in widgets such as labels, buttons, radiobuttons, and checkbuttons. *************************************** METER RESIZE (Small, Medium, or Large): The user may resize the meter rather than using a fixed meter size. The user can choose among Small, Medium, and Large meter sizes, via 3 radiobuttons on the GUI. If the user resizes the meter, by clicking on one of the S-M-L radiobuttons, the 'Refresh' function is automatically performed for the user, to force the meter to be drawn with a different sized background image --- in accordance with the theme (number) that is currently selected via the theme 'scale' widget. ************************************************* THE SHELL SCRIPT USED to update the meter pointer: A Tcl 'exec' command calls on a separate shell script --- 'get_cpu_usage_info.sh' --- that uses the file '/proc/stat' (or an equivalent file/command) to get CPU usage data --- and extract-and-format the data for return to this Tk script. This shell script is used for 3 functions of this GUI: 1) To show CPU-activity-level via the meter, for a user-specified CPU. 2) To show the CPU ID names known to this computer. 3) To present a CPU activity report for all the CPU's known to this computer. If the '/proc/stat' file is not available on your computer, you may have to edit the shell script to use a different technique or command to get the CPU data. For a computer operating system other than a Linux, Unix, BSD, or Apple-Mac system, you may need to write a C program to get appropriate CPU activity 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 ## - Put the meter(s) on their canvas(es), 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" ##+############################################################# ## Get the directory that this Tk script is in. That will be the ## directory that the 'external' utility shell script should be ## in. This directory is used to call the shell script that ## is used in the 'update_pointers' proc. ##+############################################################# ## 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. ## ## We set this before the following 'update' command, ## so that the slider-button on the scale widget is ## positioned accordingly when the GUI first appears. ##+################################################### # set WAITseconds 10.0 # set WAITseconds 5.0 set WAITseconds 2.0 # set WAITseconds 1.0 ##+############################################################# ## Set the default value for the set of S/M/Lradiobuttons. ##+############################################################# set RADVARsizeSML "SMALL" # set RADVARsizeSML "MEDIUM" # set RADVARsizeSML "LARGE" ##+#################################################### ## 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" ##+######################################################## ## Set a default CPU-ID for the entry widget. ##+######################################################## set CPUname "cpu" ##+################################################# ## Draw the meter(s) (without pointer). ##+################################################# make_meters ##+######################################################### ## Get an initial sample of the 'jiffies' data for $CPUname. ##+######################################################### foreach {CPUcheck PREVuser PREVnice PREVsystem PREVidle} \ [exec $DIRthisScript/get_cpu_usage_info.sh $CPUname] {break} ## FOR TESTING: # puts "PREVuser: $PREVuser PREVnice: $PREVnice" # puts "PREVsystem: $PREVsystem PREVidle: $PREVidle" ##+#################################################### ## Initialize the variable we use to keep track of ## the sample count. ##+#################################################### set VARsampcnt 0 ##+###################################################### ## Do an initial draw of the pointer(s), after WAITseconds ## --- to allow some time from the 'initial sample' done ## just above. ## ## We call on the proc 'update_pointers' to draw the ## pointer(s). ## ## NOTE0: ## 'update_pointers' starts a loop to keep updating the pointer(s). ## ## The proc 'update_pointers' calls itself --- ## with 'after '. ## ## NOTE1: ## The 'after' call is a ## 'recursive-like-call-with-FORKED-AND-DELAYED-execution'. ## The proc immediately returns to processing after doing the ## 'queue-command-with-delay'. ##+###################################################### set WAITmillisecs [expr {int(1000*$WAITseconds)}] after $WAITmillisecs update_pointers