A Tachometer-style Meter --- for File System Usage

uniquename - 2013nov21

In mid-2013, I put it on my 'to do' list to implement some practical applications for the nice 'tachometer style' meter for which Marco Maggi provided a demo script [1 ] back in 2003.

I have implemented two applications of this meter:

  * to show memory and swap used on my computer at any time

  * to show network activity (rate of packets transmitted and received)
    at a network interface on my computer

In the first case, the Tk script was essentially a 'wrapper' for the 'free' command, which is available on my operating system (Ubuntu 9.10, 2009 October, 'Karmic Koala').

In the 2nd case, the Tk script was essentially a 'wrapper' for the 'netstat' (or 'ifconfig') command.

The code for the memory-and-swap application is on the wiki page titled A Pair of Tachometer-style Meters --- for Memory and Swap.

The code for the network-activity application is on the wiki page titled A Pair of Tachometer-style Meters --- for Network Activity.

I had at least two other applications of the 'tachometer style' meter on my 'to-do' list --- including a file-system storage-space-usage monitoring application --- essentially a wrapper for the 'df' command. That is the subject of this page.

Since the 'df' command is probably available on most Linux, Unix, and BSD systems --- and since the Apple Mac operating system is based on a BSD system, this 'file-system-usage' utility is probably usable (with very little change) on Linux-Unix-BSD-Mac systems.

The 'df -kl' command returns data like

   $ df -kl
   Filesystem           1K-blocks      Used Available Use% Mounted on
   /dev/sda1             74611932  59225272  11596528  84% /
   udev                   1677060       328   1676732   1% /dev
   none                   1677060       264   1676796   1% /dev/shm
   none                   1677060        84   1676976   1% /var/run
   none                   1677060         0   1677060   0% /var/lock
   none                   1677060         0   1677060   0% /lib/init/rw
   /dev/sdb5             76920384  66102948   6910032  91% /home

In this case, you can see that there are two major file systems on this computer --- 'Mounted on' the root directory (/) and the 'home directories' directory (/home). On this computer, there are 2 disk drives --- one used for the 'root' file system, and one used for the 'home directories' file system.

However, on many home computers, there is only one major file system --- the 'root' file system.

So in designing this GUI, I decided to go with a single meter on the GUI --- rather than 2 meters, like in the 'memory-and-swap' and the 'network-activity' Tk GUI's that I had devised before.

Besides showing a 'percent-usage' meter (on a Tk canvas on the GUI), I wanted to provide a means for the user to query the mount points available on the computer --- and to choose one of the mount points to monitor.

And I wanted to provide a Tk 'scale' widget on the GUI, by which the user can specify, at any time, a new 'sampling rate' --- for getting the usage data via the 'df' command. (Actually, the 'sampling rate' is a 'wait-time' = 'wave-length', rather than a 'frequency'.)

I was able to implement that ability by use of the Tcl 'after' command, in the form

   after $WAITmillisecs update_needles

This command is issued once to initialize the GUI --- and it is issued within the 'update_needles' proc itself, to continue getting storage-space usage data for a user-specified mount point via the 'df' command.

Since, typically, the '%-used' storage space of a large file system is changing very gradually, a sampling rate larger than a minute or two may suffice for most needs. So the scale is initialized at a rate of that magnitude.

I also wanted to supply a 'Refresh' button on the GUI, so that the user can request at ANY TIME, a new query of the usage data for the file system being monitored.

And, in case there is more than one file system that is 'near-full', I wanted to supply a 'Report' button on the GUI, so that the user can see, at any time, the usage of ALL the file systems on the computer.

In putting together the code for this GUI, I drew heavily on the 'shadow-circle' technique of Marco Maggi to make nice looking meters. And I included a 'red-line' (danger) area on the meter, like he did.

One rather unique thing about this implementation of the meter (something not done by Maggi in his demo) is that the GUI window and the canvas and the meter are resizable. In other words, I spent quite a bit of effort in converting Maggi's procs

  • FROM using hard-coded numbers for making the meters and their needles
  • TO using variables that work off of queries on the current size of frame and canvas widgets.

So the user is able to resize the window and click on the 'Refresh' button to get a bigger version of the meters and the needle position.

After several iterations, during which I decided on implementing the features described above, I ended up with the GUI seen in the following image.

meter_filesysUsage_root_screenshot_293x371.jpg

When the GUI first comes up, the 'MonitorMount' is defaulted to the 'root' file system --- since that file system is defined on all of the target computers of this utility.

Below is an image that shows that one can change the 'MonitorMount' entry field so that a different file system is monitored --- in this case the 'home directories' file system mounted at /home.

meter_filesysUsage_home_screenshot_308x372.jpg

The 'ShowMounts' button on the GUI can be used to show the available mount points. A small window pops up showing a list of mount directories.

When the GUI first comes up, the meter (its canvas) is sized at about 200x200 pixels --- and the data shown is based on an initial execution of the 'df' command --- from which data for the 'root' file system is extracted.

You can see the page A Pair of Tachometer-style Meters --- for Memory and Swap to see an example image that demonstrates that the user is able to resize the window and click on the 'Refresh' button to get a bigger version of the meters and the needle position.

I had hoped to come up with a technique to avoid the 'aliasing' effect on the needles --- but I did not do it for the 'memory-and-swap' script --- and I have still not done that for the 'network-activity' script and this script.

However, as 'retina display' monitors come into use more and more (with resolutions above about 2000x1500 pixels), even without changing the needle-drawing code in this script, you may find that the 'jaggies' are hard to see. (I do not have such a monitor yet, so I cannot say for sure.)


CAPTURING THE GENERATED IMAGE:

When you get an image that you want to save --- for example, the images that I posted above to show some GUI features of this utility --- 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 presentation in an email or on a web page.


The code

Below, I provide the Tk script code for this 'file-system percent-used' display utility.

I follow my usual 'canonical' structure for Tk code for this Tk script:

  0) Set general window & widget parms (win-name, win-position,
     win-color-scheme, fonts, widget-geometry-parms, win-size-control,
     text-array-for-labels-etc).

  1a) Define ALL frames (and sub-frames, if any).
  1b) Pack   ALL frames and sub-frames.

  2) Define & pack all widgets in the frames, frame by frame.
              Within each frame, define ALL the widgets.
              Then pack the widgets.

  3) Define keyboard and mouse/touchpad/touch-sensitive-screen action
     BINDINGS, if needed.

  4) Define PROCS, if needed.

  5) Additional GUI initialization (typically with one or more of
     the procs), if needed.

This Tk coding structure is discussed in more detail on the page A Canonical Structure for Tk Code --- and variations.

This structure makes it easy for me to find code sections --- while generating and testing a Tk script, and when looking for code snippets to include in other scripts (code re-use).

I call your attention to step-zero. One thing that I have started doing in 2013 is using a text-array for text in labels, buttons, and other widgets in the GUI. This can make it easier for people to internationalize my scripts. I will be using a text-array like this in most of my scripts in the future.


Experimenting with the GUI

As in all my scripts that use the 'pack' geometry manager (which is all of my 100-plus scripts, so far), I provide the four main pack parameters --- '-side', '-anchor', '-fill', '-expand' --- on all of the 'pack' commands for the frames and widgets.

That helps me when I am initially testing the behavior of a GUI (the various widgets within it) as I resize the main window.

I think that I have used a pretty nice choice of the 'pack' parameters. The label and button widgets stay fixed in size and relative-location if the window is re-sized --- while the canvas area (without scroll bars) expands/contracts whenever the window is re-sized, and the 'Refresh' button is clicked.

The meter expands/contracts when the window is re-sized --- but perhaps not always in a way you would desire. Occasionally, you may need to tug the borders of the window to show the meter in a way that suits you.

You can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various frames and widgets --- to get the widget behavior that you want.

And you can look into the code that is drawing the meter to see if you can devise meter-resizing behavior that pleases you more.

___

Additional experimentation: You might want to change the fonts used for the various GUI widgets. For example, you could change '-weight' from 'bold' to 'normal' --- or '-slant' from 'roman' to 'italic'. Or change font families.

In fact, you may NEED to change the font families, because the families I used may not be available on your computer --- and the default font that the 'wish' interpreter chooses may not be very pleasing.

I use variables to set geometry parameters of widgets --- parameters such as border-widths and padding. And I have included the '-relief' parameter on the definitions of frames and widgets. Feel free to experiment with those 'appearance' parameters as well.

If you find the gray 'palette' of the GUI is not to your liking, you can change the value of the RGB parameter supplied to the 'tk_setPalette' command near the top of the code.


Some features in the code

That said, here's the code --- with plenty of comments to describe what most of the code-sections are doing.

You can look at the top of the PROCS section of the code to see a list of the procs used in this script, along with brief descriptions of how they are called and what they do.

The main procs are

   'make_tachometers' - to draw one (or more) meters within their Tk (square) canvases.

                        We allow the canvas(es) to resize according to
                        a resizing of the window. This proc will set the
                        SQUARE size of the canvas(es) according to the current
                        size of the frame containing the canvas(es).

   'make_one_tachometer' - called by 'make_tachometers', to make each meter.

                           In this utility, we draw only one meter --- but the
                           procs are structured to facilitate adding another
                           meter, if a Tcl-Tk coder so desired.

   'draw_rivet'          - called by 'make_one_tachometer', 4 times, to put
                           rivets in 4 corners around a meter.

   'draw_circle_shadow'  - called by 'make_one_tachometer' to put a shadowed
                           edge around the circle that makes the meter.
                           Also called to help make a 'pin' in the center of
                           the meter to hold the needle. Also called to make
                           the 4 rivets.

   'update_needles'      - to update the needle(s) on the meter(s).

   'update_one_needle'   - called by 'update_needles', to draw each needle.

   'Refresh'             - called by 'Refresh' button. Runs 'make_tachometers'
                           and 'update_needles'.

   'Report'              - called by 'Report' button.

   'show_mounts'         - called by 'ShowMounts' button.

   'popup_msgVarWithScroll' - called by the 'Help' button, to show text in
                              variable $HELPtext. Also used by the 'Report'
                              and 'show_mounts' procs.

Thanks to Marco Maggi whose 'shadow-circle' drawing technique and code made this script much easier to write.


It is my hope that the copious comments in the code will help Tcl-Tk coding 'newbies' get started in making GUI's like this.

Without the comments, potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch videos of people (who shouldn't be rapping) rapping.


 Code for Tk script 'meters_forFileSystems_usage.tk' (the Tk 'wrapper') :
#!/usr/bin/wish -f
##+########################################################################
##
## SCRIPT: meters_forFileSystems_usage.tk
##
## PURPOSE: This script is meant to show a GUI with a
##          tachometer-style METER which shows the PERCENT-USED
##          storage SPACE of a FILE-SYSTEM --- typically, for the file-system
##          (i.e. disk partition) with the least free-space remaining.
##
##          In other words, the meter shows the RATIO of used-space
##          to total-allocated-space --- for a file-system. Typically, the
##          user wants to check %-used for the file system for which the
##          'used-space-ratio' is the  highest.
##
##          This utility is designed for computer operating systems of the
##          Linux/Unix/BSD type --- computers that have a 'df' command and
##          commands like awk, grep, sed, and sort. The 'df' command
##          displays file-system space-usage info for the file system
##          mount points of a Unix/BSD/Linux type file system.
##
##          On computers with a single, 'root' file system, the user would
##          typically want the value shown on the meter to be for that
##          one 'root' file system.
##
##          On computers with both a 'root' (/) and a 'home' (/home)
##          file system (and/or other file systems), the user will typically
##          want the value shown on the meter to be for the file system with
##          the highest 'percent-used'.
##
##          Or, if TWO meters are to be shown, the user would typically
##          want to show the percent used of the 'root' file system and
##          the percent used of another file system with the highest
##          percent used --- or the 2nd-highest percent used, if the
##          'root' file system is the 'fullest' file system.
##
##          To simplify, we show just one meter (for a user-specified
##          file system mount point) rather than two or more meters,
##          for multiple file systems on the computer.
##
##          When the GUI first appears, the meter may show the %-used
##          for the 'root' file system.
##
##          Of course, a Tck-Tk programmer could change this script to initially
##          show the %-used for the 'most full' file system --- AND the
##          GUI could be changed to show more than meter at a time. For
##          relative simplicity of coding, we do not do that in this version.
##
## USAGE of a USER-SPECIFIED MOUNT POINT:
##
##          This script was developed on Linux and uses the 'df' command
##          to get 'percent-used' data to locate the position of the
##          meter needle for a user-specified mount point. A pop-down/out
##          menu of the available mount points is provided on the GUI,
##          Thus the user can choose the file system whose %-used is shown
##          on the meter.
##
##          A red-line area of the meter indicates that when the
##          %-used gets above about 85%, the user should take heed.
##
##          The tota-allocated and total-used space (in Gigabytes)
##          is also shown as text just above or below the meter.
##
## AUTO-UPDATE of the METER and TEXT-DATA for the mount point:
##
##          The needle on the meter may be updated periodically to
##          show the percent of storage space currently in-use,
##          for a file system that needs monitoring.
##
##          The amount of time between updates is controlled by a
##          scale widget on the GUI --- defaulted to 60 or 120 seconds,
##          say (or more). And an 'Update'/'Refresh' button on the GUI
##          can be used to force an update at any moment.
##
## A 'REPORT' OPTION:
##
##          Also the 'df' command can be used to show space 'allocated' and
##          'used' and '%-used' data, for ALL the mount points of
##          the computer. 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.
##
##+################
## GUI DESCRIPTION:
##
##         This script provides a Tk GUI with the following widgets.
##
##         1) There is an 'fRbuttons' frame to hold BUTTONS such as
##            'Exit' 'Help' 'Report' buttons, In addition, there is a
##            'Refresh' button --- to 'manually' issue the 'df' command and
##            update the needle display on the meter.
##
##            The is also a 'Mounts' button to show the user the list
##            of available directory mount-points. Next to that button
##            is an entry widget in which to enter a mount-point. That
##            mount-point is the one whose used-data is shown on the meter. 
##
##            (In this 'buttons' frame, there may also be a SCALE widget
##             for the user to set a 'wait-seconds' for auto-refresh of
##             the meter data. The 'wait' may  be in seconds --- down to
##             tenths of seconds, and up to multiple minutes. The scale
##             could be set to an initial value for the auto-refresh
##             --- say, an initial value of 5 minutes.
##
##             The auto-refresh feature is, typically, not so useful for
##             filesystem space, which, typically, is not changing much
##             within seconds --- or even within many minutes.
##
##             However, if a user is running a job that is creating
##             many files or a huge file in a file system, then the
##             auto-refresh feature may be useful --- to monitor the
##             consumption of space in a file system.)
##
##         2) There is an 'fRmeters' frame to contain a CANVAS widget
##            that holds the meter image, in a SQUARE canvas.
##            (Someday, we may wish to put two or more meters in the
##            'fRmeters' frame --- to show percent-used for at least
##             two file systems.)
##
##            The 'fRmeters' frame also holds some LABEL widgets, to show
##            TOTAL ('allocated') and USED storage space, as text items
##            --- for any given file system.
##
##+################################
## METHOD USED to update the meter(s):
##
##    A Tcl 'exec' command calls on a separate shell script that uses
##    the 'df' command to get the storage space data for the file-systems
##    of the computer and reformat the data for return to this Tk script.
##
##    The shell script will be capable of returning info of 3 types:
##      1) a report of the status of ALL the file systems known to 'df'
##      2) a list of the file-system mount-points
##      3) %-used, allocated-Gigabytes, used-Gigabytes for a user-specified
##         mount-point.
##
##+#######################
## CAPTURING THE GUI IMAGE:
##
##   There are not many scenarios in which a user would want to
##   send an image of the %-used meter(s) to anyone, but in case
##   the user would ever want to capture an 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, with the editor --- 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
##     '.fRcontrol1' - to contain a 'Mounts' button --- and an entry widget
##     '.fRcontrol2' - to contain a scale widget, with a label widget.
##     '.fRmeters'   - to contain square canvas widget(s), which will display
##                     the the meter(s) --- and perhaps more than one
##                     meter someday.
##
##   Sub-frames:
##       '.fRmeters.fRmeter1' - for 2 label widgets & 1 canvas widget
##
##  1b) Pack the frames.
##      (The canvas sub-frames may be packed as needed, if a variable
##       number of meters is ever supported.)
##
##  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:  none
##
##  4) Define PROCS:
##
##    'make_tachometers' - to draw one or more meters within Tk (square)
##                         canvases.
##
##                        (We allow the canvas(es) to resize according to
##                         a resizing of the window. This proc will set the
##                         SQUARE size of the canvases according to the current
##                         size of the frame containing the canvases. That
##                         frame will be allowed to expand if the user
##                         expands the entire GUI window.)
##
##    'make_one_tachometer' - called by 'make_tachometers', to make each meter.
##
##    'draw_rivet'      - called by 'make_one_tachometer', 4 times, to put
##                        rivets in 4 corners around a meter.
##
##    'draw_circle_shadow'  - called by 'make_one_tachometer' to put a shadowed
##                            edge around the circle that makes the meter.
##                            Also called to help make a 'pin' in the center of
##                            the meter to hold the needle. Also called,
##                            in 'draw_rivet', to make each of the rivets.
##
##    'update_needles'      - to update the needles on the one or more meters.
##
##    'update_one_needle'   - called by 'update_needles', to draw the
##                            needle on each meter.
##
##    'Refresh'             - called by 'Refresh' button. Runs
##                            'make_tachometers' and 'update_needles'.                     
##
##   'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var.
##                              Also used by the 'Report' button and the
##                              'Mounts' button.
##
##  5) Additional GUI Initialization:
##        - call 'make_tachometers' to put the meter(s) on the canvas(es)
##        - call 'update_needles' --- to initialize the needle 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 2013sep03 Started the code on Linux,
##                                        Ubuntu 9.10.
## Changed by: Blaise Montandon 2013oct31 Decided to use one meter and allow
##                                        the user to choose which file-system
##                                        will be monitored with the meter.
## Changed by: Blaise Montandon 2013nov20 Added the 'Mounts' button and entry
##                                        widgets, added a couple of procs, and
##                                        started testing the GUI and procs.
## Changed by: Blaise Montandon 2013nov21 Revise the way the DIRscripts var
##                                        is set.
## Changed by: Blaise Montandon 2013nov24 1) Changed the arc-size for the
##                                           'create arc' command of the
##                                           'make_one_tachometer' proc so that the
##                                           red-line arc extends over 15 percent
##                                           (or 10 percent) rather than about 
##                                           5 percent.
##                                        2) Changed the 'update_one_needle' proc
##                                           to calculate %-used from the
##                                           'Available' number from 'df' rather
##                                           than the 'Used' number from 'df'.
##                                        3) Touched up some comments --- mostly
##                                           'meters' to 'meter(s)'.
##                                        4) Added a 'bind' command for the
##                                           'entryMOUNT' entry field.
##                                        5) Added a '%' text item toward the
##                                           bottom of the meter canvas.
##+########################################################################

##+######################################################
## Set WINDOW TITLE and POSITION.
##+######################################################

wm title    . "FileSystem Space Use"
wm iconname . "FilesysUse"

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 listboxBKGD "#ffffff"
  set entryBKGD "#ffffff"
  set textBKGD  "#f0f0f0"
# set scaleBKGD "#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 the text in LISTBOX lists,
## for text in ENTRY fields --- and often for text in a
## TEXT widget.
##+########################################################

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


## 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


## 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' and '.fRmeters'
## 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 '.fRmeter'  frame.
##+#####################################################################

## FOR WIDTH:

set minWidthPx [font measure fontTEMP_varwidth \
   " Exit  Help  Report  Refresh "]

## If we use the 'wait-seconds' scale widget, we would accomodate a label
## and add pixels for length of the scale widget, at least 100.
##
## For now, we simply add some pixels to account for right-left-size of
## window-manager decoration (~8 pixels) and some pixels for
## frame/widget borders (~5 widgets x 4 pixels/widget = 20 pixels).

set minWinWidthPx [expr {28 + $minWidthPx}]


## For HEIGHT --- for
##    1 char   high for 'fRbuttons'
##    2 chars  high for 'fRcontrols'
##   50 pixels high for 'fRmeters'

set charHeightPx [font metrics fontTEMP_varwidth -linespace]

set minWinHeightPx [expr {3 * $charHeightPx}]

## Add about 50 pixels for height of the canvas
## AND add about 20 pixels for top-bottom window decoration --
## and some pixels for top-and-bottom of frame/widget borders
## (~4 widgets x 4 pixels/widget = 16 pixels).

set minWinHeightPx [expr {86 + $minWinHeightPx}]


## FOR TESTING:
#   puts "minWinWidthPx = $minWinWidthPx"
#   puts "minWinHeightPx = $minWinHeightPx"

wm minsize . $minWinWidthPx $minWinHeightPx


## We may allow the window to be resizable.  We pack the canvases
## (and the frames that contain them) with '-fill both -expand 1'
## so that the canvases can be enlarged by enlarging the window.

## If you want to make the window un-resizable, 
## you can use the following statement.
#   wm resizable . 0 0


##+##############################################################
## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI.
##     NOTE: This can aid INTERNATIONALIZATION. This array can
##           be set according to a nation/region parameter.
##+##############################################################

## if { "$VARlocale" == "en"}

## For '.fRbuttons' frame:

set aRtext(buttonEXIT) "Exit"
set aRtext(buttonHELP) "Help"
set aRtext(buttonREPORT)  "Report"
set aRtext(buttonREFRESH) "Refresh"

## For '.fRcontrol1' frame:

set aRtext(buttonMOUNTS) "ShowMounts"
set aRtext(labelMOUNT)   "   MonitorMount:"

## For '.fRcontrol2' frame:

set aRtext(labelSCALE)  "SampleRate
(seconds) :"

## END OF  if { "$VARlocale" == "en"}


##+################################################################
## DEFINE *ALL* THE FRAMES:
##
##   Top-level : '.fRbuttons' , '.fRcontrols', '.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 .fRcontrol1 -relief $RELIEF_frame  -bd $BDwidth_frame
frame .fRcontrol2 -relief $RELIEF_frame  -bd $BDwidth_frame
frame .fRmeters   -relief $RELIEF_frame  -bd $BDwidth_frame


##+##########################################################
## SUB-FRAME DEFINITIONS:
##+##########################################################

set MAXmeters 1
# set MAXmeters 2

frame .fRmeters.fRmeter1  -relief raised  -bd 2

# frame .fRmeters.fRmeter2  -relief raised  -bd 2



##+##############################
## PACK the top-level FRAMES. 
##+##############################

pack .fRbuttons \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRcontrol1 \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRcontrol2 \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRmeters \
   -side top \
   -anchor nw \
   -fill both \
   -expand 1


##+######################################################
## We pack a single meter sub-frame, for now.
##
## Someday we may allow for at least two meters.
## However, we do not want the additional meters
## to show if they are not going to be used --- for
## example, on computers with only one (root) file system.
##+######################################################

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}


## Pack the widgets in frame '.fRbuttons'.

pack .fRbuttons.buttEXIT \
     .fRbuttons.buttHELP \
     .fRbuttons.buttREPORT \
     .fRbuttons.buttREFRESH \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+##########################################################
## In FRAME '.fRcontrol1' -
## DEFINE-and-PACK a 'ShowMounts' BUTTON,
## a mounts ENTRY widget.
##+##########################################################

## Here is the BUTTON for showing mount-points of file-systems.

button .fRcontrol1.buttMOUNTS \
   -text "$aRtext(buttonMOUNTS)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {show_mounts}

label .fRcontrol1.labelMOUNT \
   -text "$aRtext(labelMOUNT)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -bd $BDwidthPx_label

## Here is the ENTRY field for the filesys-mount-point.

## Set this widget var in the GUI initialization section
## at the bottom of this script.
# set MOUNTname "/"

entry .fRcontrol1.entryMOUNT \
   -textvariable MOUNTname \
   -bg $entryBKGD \
   -font fontTEMP_fixedwidth \
   -width 9 \
   -relief sunken \
   -bd $BDwidthPx_entry

## Pack the widgets in frame '.fRcontrol1'.

pack .fRcontrol1.buttMOUNTS \
     .fRcontrol1.labelMOUNT \
     .fRcontrol1.entryMOUNT \
   -side left \
   -anchor w \
   -fill none \
   -expand 0

##+##########################################################
## In FRAME '.fRcontrol2' -
## DEFINE-and-PACK a LABEL-AND-SCALE widget pair ---
## for changing the 'refresh rate' for the meter needle(s).
##+##########################################################

## Here is the LABEL-AND-SCALE pair for the wait-seconds (sample rate).

label .fRcontrol2.labelSCALE \
   -text "$aRtext(labelSCALE)" \
   -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 .fRcontrol2.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 .fRcontrol2.labelCOUNT \
   -textvariable VARsampcnt \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd 0


## Pack the widgets in frame '.fRcontrol2'.

pack .fRcontrol2.labelSCALE \
     .fRcontrol2.scaleSECONDS \
     .fRcontrol2.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 set highlightthickness & borderwidth of the canvas to
## zero, as suggested on page 558, Chapter 37, 'The Canvas
## Widget', in the 4th edition of the book 'Practical
## Programming in Tcl and Tk'.
##+#######################################################

label .fRmeters.fRmeter1.labelINFO1 \
   -text "" \
   -font fontTEMP_SMALL_varwidth \
   -justify left \
   -anchor w \
   -relief raised \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -bd $BDwidthPx_label

label .fRmeters.fRmeter1.labelINFO2 \
   -text "" \
   -font fontTEMP_SMALL_varwidth \
   -justify left \
   -anchor w \
   -relief raised \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -bd $BDwidthPx_label

label .fRmeters.fRmeter1.labelINFO3 \
   -text "" \
   -font fontTEMP_SMALL_varwidth \
   -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 \
     .fRmeters.fRmeter1.labelINFO3 \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRmeters.fRmeter1.can \
   -side top \
   -anchor nw \
   -fill none \
   -expand 0


##+##################################################
## END OF DEFINITION of the GUI widgets.
##+##################################################
## Start of BINDINGS, PROCS, Added-GUI-INIT sections.
##+##################################################

##+##################################################################
##+##################################################################
##  BINDINGS SECTION:
##+##################################################################

bind .fRcontrol1.entryMOUNT <Return> {Refresh}


##+##################################################################
##+##################################################################
## DEFINE PROCS SECTION:
##
##    'make_tachometers' - to draw meter(s) within Tk (square) canvas(es).
##
##                         We allow the Tk canvas(es) to resize according to
##                         a resizing of the window. This proc will draw the
##                         meter/s with size based on the current size of the
##                         'fRmeters' frame.
##
##    'make_one_tachometer' - to draw one tachometer. Called by 'make_tachometers'
##                            to make the meter(s).
##
##    'draw_rivet'          - called by 'make_tachometers' to put rivets in 4
##                            corners around each meter.
##
##    'draw_circle_shadow'  - called by 'make_tachometers' to put a shadowed
##                            edge around the circle that makes each meter.
##                            Also called by 'make_tachometers' to put
##                            a shadowed edge on the 'pin' that holds a needle.
##                            Also called by 'draw_rivet' to put a shadowed
##                            edge on each rivet.
##
##    'update_needles'     - to update the needle(s) on the meter(s).
##                           Called initially at the bottom of this script.
##                           And called in the 'Refresh' proc.
##
##   'update_one_needle'   - to draw one needle in a specified meter-canvas.
##                           Called by 'update_needles' to update each of the
##                           needles on the one or more meters.
##
##   'Refresh'         - called by the 'Refresh' button. Runs the procs
##                       'make_tachometers' and 'update_needles' --- in particular,
##                       for the user to force the meter(s) to be resized if
##                       the user resizes the window --- and whenever the user
##                       wants a new 'reading'.
##
##   'Report'          - called by the 'Report' button. This Report proc
##                       calls on the 'get_fileSystems_spaceData.sh' script
##                       with the 'all' parameter.
##
##   'show_mounts'     - called by the 'ShowMounts' button. This proc
##                       calls on the 'get_fileSystems_spaceData.sh' script
##                       with the 'mounts' parameter.
##
## 'popup_msgVarWithScroll' - to show the HELPtext var. Called by the 'Help' button.
##                            Also used by the 'Report' and 'ShowMounts' buttons.
##+############################################################################


##+########################################################################
## PROC 'make_tachometers'
##+########################################################################
## PURPOSE: Draws all features of one or more tachometer-style meters (except
##          the needles) --- in a 'nice filling-size' according to the
##          current canvas dimensions.
##
##             We will allow the canvas(es) to resize according to
##             a resizing of the window. This proc will redraw the
##             meter(s) in proportion to the new size of the canvas.
##
## CALLED BY: once, at the 'Additional GUI Initialization' section,
##            at the bottom of this script --- and
##            in the 'ReDraw...' proc.
##+########################################################################

set Nmeters 1

proc make_tachometers {} {

   global Nmeters marginPx

   ## 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 is packed with '-fill both' and '-expand 1'.
   ############################################################

   set curMetersFrameWidthPx  [winfo width  .fRmeters]
   set curMetersFrameHeightPx [winfo height .fRmeters]

   ## Adjust height for 3 text lines in the '.fRmeters' frame
   ## for total,used,available space in the file system.

   set charHeightPx [font metrics fontTEMP_varwidth -linespace]
   set curMetersFrameHeightPx \
       [expr {$curMetersFrameHeightPx - (3 * $charHeightPx)}]


   ############################################################
   ## Set a width-and-height to use for each (square) canvas
   ## to contain the one (or more) meters.
   ## (We take ~6 pixels off the width to account for some
   ##  borderwidths of frames within the 'fRcanvases' frame.) 
   ############################################################

   # set meterSizePx [expr {int(($curMetersFrameWidthPx - 6) / $Nmeters)}]
   # if {$curMetersFrameHeightPx < $meterSizePx} {set meterSizePx $curMetersFrameHeightPx}

   set meterSizePx [expr {$curMetersFrameHeightPx - 6}]


   ####################################################################
   ## Resize the canvas(es) --- like '.fRmeters.fRmeter1.can' --- that
   ## hold the one (or more) square Tk canvases for the meter(s).
   ## Note that this/these canvases and their parent frames were all packed
   ## with '-fill both -expand 1' --- so if the canvas widgets
   ## expand/contract, then the parent frames should do the same.
   ####################################################################

   # for {set i 1} {$i < $Nmeters} {incr i} {
   #    eval .fRmeters.fRmeter$i.can configure -width  $meterSizePx
   #    eval .fRmeters.fRmeter$i.can configure -height $meterSizePx
   # }

   .fRmeters.fRmeter1.can configure -width  $meterSizePx
   .fRmeters.fRmeter1.can configure -height $meterSizePx


   if {0} {
      ## Not needed?? Resizing the Tk canvases will determine the
      ## size of the 'parent' frames of the canvases??
      for {set i 1} {$i < $Nmeters} {incr i} { 
         eval .fRmeters.fRmeter$i configure -width  $meterSizePx
         eval .fRmeters.fRmeter$i configure -height $meterSizePx
      }
   }


   if {0} {
      ## Not needed?? Resizing the Tk canvases will determine the
      ## size of the 'parent' frames of the canvases??
      set fullWidthPx [expr {($Nmeters * ($meterSizePx + 4)}]
      .fRmeters configure -width  $fullWidthPx
      .fRmeters configure -height $meterSizePx
   }

   #########################################################
   ## Draw the Nmeters meters (without their needles).
   #########################################################

   # for {set i 1} {$i < $Nmeters} {incr i} {
   #    eval make_one_tachometer .fRmeters.fRmeter$i.can
   # }

   #####################################################
   ## NEEDED to force the canvas(es) and frames to update
   ## according to the new canvas size(s).
   #####################################################

   update

   #########################################################
   ## Draw meter1 (without needle).
   #########################################################

   make_one_tachometer .fRmeters.fRmeter1.can

}
## END OF proc 'make_tachometers'


##+########################################################################
## PROC 'make_one_tachometer'
##+########################################################################
## PURPOSE: Draws all features of a tachometer-style meter (except the
##          needle) --- according to the 'marginPx' parameter to set
##          top-right and bottom-left coordinates to specify the location
##          of the square exactly containing the circular meter on
##          the canvas whose ID is passed into this proc.
##
##  The features include:
##      - white-filled circle for the meter background
##      - a gray-shaded (shadowed) edge around the circle
##      - a 'pin' in the center of the circle, for the needle
##      - 4 decorative rivets at the corners of the canvas
##      - an arc with tic-marks
##      - a red danger-zone in the last segment of the arc
##        (between the last pair of tic-marks)
##      - labels for the tic-marks
##
## CALLED BY: proc 'make_tachometers'
##+#######################################################################

## Set an 'indentation' to use for placing the outer-circle of meter(s)
## from the 4 edges of their respective canvases.

set marginPx 12

set pi [expr {4.0 * atan(1.0)}]
set radsPERdeg [expr {$pi/180.0}]

set Nsegs 10
set pcentLabels "0 10 20 30 40 50 60 70 80 90 100"

## The above variables are set ONCE, for use in the following proc.

proc make_one_tachometer {canvas} {

   global marginPx pi radsPERdeg Nsegs pcentLabels

   ## FOR TESTING: (to dummy out this proc)
   #   return
 
   ################################################################
   ## Remove any previously drawn elements in this canvas, if any.
   ################################################################

   catch {$canvas delete all}


   ##################################################################
   ## Get the width (= height) of the specified (square) canvas.
   ##################################################################

   set curCanvasSizePx  [winfo width  $canvas]

   ##################################################################
   ## Set the corner coords for drawing the meter circle (background).
   ##################################################################

   set topleftXpx $marginPx
   set topleftYpx $marginPx
   set botrightXpx [expr {$curCanvasSizePx - $marginPx}]
   set botrightYpx [expr {$curCanvasSizePx - $marginPx}]


   ################################################
   ## Draw basic white-filled circle for the meter.
   ################################################

   $canvas create oval \
      $topleftXpx $topleftYpx $botrightXpx $botrightYpx \
      -fill white -outline {}

   # -width 1 -outline lightgray

   ## FOR TESTING: (exit this proc before adding more to the meter)
   #   return


   #######################################################################
   ## Draw shadow-circle at the outer circle of the meter.
   #######################################################################
   ## INPUTS:
   ## - the 4 corner coordinates of the oval/circle box (in pixels)
   ## - number of segments for the arc (segments of differing color shade)
   ## - width (in pixels) to draw the arc segments
   ## - start angle for drawing the (darker) arc segments
   ##   (measured counter-clockwise from the 3 o'clock position)
   ##   (An angle of +135=90+45 means the dark side of the 'shadow-circle'
   ##    is on the north-west side of the circle.)
   ######################################################################

   draw_circle_shadow $canvas \
      $topleftXpx $topleftYpx $botrightXpx $botrightYpx \
      40 6 135.0
 
   ## FOR TESTING: (exit this proc before adding more to the meter)
   #   return


   ###################################################################
   ## Draw a shadow-circle for the 'pin' of the meter needle.
   ###################################################################
   ## INPUTS:
   ## - the 4 corner coordinates of the oval/circle box (in pixels)
   ## - number of segments for the arc (segments of differing color shade)
   ## - width (in pixels) to draw the arc segments
   ## - start angle for drawing the (darker) arc segments
   ##    (measured counter-clockwise from the 3 o'clock position)
   ##    (An angle of -45 means the dark side of the 'shadow-circle'
   ##     is on the south-east side of the circle.)
   ###################################################################

   set centerXpx [expr {int($curCanvasSizePx/2.0)}]
   set centerYpx $centerXpx

   set pinOuterRadiusPx 14

   set x1 [expr {$centerXpx - $pinOuterRadiusPx}]
   set y1 [expr {$centerYpx - $pinOuterRadiusPx}]
   set x2 [expr {$centerXpx + $pinOuterRadiusPx}]
   set y2 [expr {$centerYpx + $pinOuterRadiusPx}]

   draw_circle_shadow $canvas $x1 $y1 $x2 $y2 40 6 -45.0

   ## FOR TESTING: (exit this proc before adding more to the meter)
   #   return

   ## FOR TESTING:
   #    puts "proc 'make_one_tachometer'."
   #    puts "curCanvasSizePx: $curCanvasSizePx   centerYpx: $centerYpx"

   ############################################################
   ## Draw a red-filled circle on the 'pin' of the meter needle.
   ############################################################

   set pinRadiusPx 12

   set x1 [expr {$centerXpx - $pinRadiusPx}]
   set y1 [expr {$centerYpx - $pinRadiusPx}]
   set x2 [expr {$centerXpx + $pinRadiusPx}]
   set y2 [expr {$centerYpx + $pinRadiusPx}]

   $canvas create oval \
      $x1 $y1 $x2 $y2 -fill red -outline {}

   #   -width 1 -outline lightgray

   ## FOR TESTING: (exit this proc before adding more to the meter)
   #   return


   ###########################################
   ## Draw arc-line on which to put tic marks.
   #################################################
   ## 320 degrees counter-clockwise from -70 degrees
   ## (based at 3 oclock) is 70 degrees beyond 180,
   ## going counter-clockwise.
   ## I.e. -70 + 320 = 250 = 180 + 70
   #################################################

   set arcLineIndentPx 10

   set x1 [expr {$topleftXpx  + $arcLineIndentPx}]
   set y1 [expr {$topleftYpx  + $arcLineIndentPx}]
   set x2 [expr {$botrightXpx - $arcLineIndentPx}]
   set y2 [expr {$botrightYpx - $arcLineIndentPx}]

   $canvas create arc $x1 $y1 $x2 $y2 \
      -start -70 -extent 320 -style arc \
      -outline black -width 2

   ## FOR TESTING: (exit this proc before adding more to the meter)
   #   return


   ##################################################
   ## Draw tic-marks and labels around the meter.
   ##################################################

   set DEGperTIC [expr {320.0/$Nsegs}]
   set half  $centerXpx

   ##  outer location (radius) of tic marks
   set l1    [expr {$half - ($arcLineIndentPx + $marginPx)}]

   ##  inner location (radius) of tic marks
   set l2    [expr {$l1 - $arcLineIndentPx}]

   ## inner location of tic labels
   set l3    [expr {$l2 - $arcLineIndentPx}]
 
   set angle0  250.0

   for {set i 0} {$i <= $Nsegs} {incr i} {

      set rads [expr {($angle0 - ($DEGperTIC * $i)) * $radsPERdeg}]
 
      set x1 [expr {$half + $l1 * cos($rads)}]
      set y1 [expr {$half - $l1 * sin($rads)}]
      set x2 [expr {$half + $l2 * cos($rads)}]
      set y2 [expr {$half - $l2 * sin($rads)}]

      $canvas  create line \
         $x1 $y1 $x2 $y2 \
         -fill black -width 2
 
      set x1 [expr {$half + $l3 * cos($rads)}]
      set y1 [expr {$half - $l3 * sin($rads)}]
 
      set label [lindex $pcentLabels $i]

      if { [string length $label] } {
         $canvas create text \
            $x1 $y1 \
            -anchor center -justify center -fill black \
            -text $label -font { Helvetica 10 }
      }
      ## END OF labels loop.

   }
   ## END OF i-loop for tic-marks

   ## FOR TESTING: (exit this proc before adding more to the meter)
   #   return


   ##################################################
   ## Draw a percent sign (%) at the bottom of the meter.
   ##################################################

   set x1 $half
   set y1 [expr {$half + (0.65 * $l3)}]

   $canvas create text \
      $x1 $y1 \
      -anchor center -justify center -fill black \
      -text "%" -font { Helvetica 14 }

   #######################################################
   ## Draw red-line arc-segment (danger zone) of the meter.
   #######################################################
   ## Recall that the '-start' of 'create arc' is at
   ## the 3 o'clock (east) position.
   ## We have the arc of 0 to 100 percent going from 250
   ## degrees to -70 degrees as we traverse the 'dial'
   ## clockwise.
   ## 
   ## So we can draw the red-line from -70 a certain
   ## number of degrees (say a tic-mark=10-percent or 
   ## 1.5-tic-marks=15-percent) going clockwise.
   #################################################

   set redLineIndentPx 15

   set x1 [expr {$topleftXpx  + $redLineIndentPx}]
   set y1 [expr {$topleftYpx  + $redLineIndentPx}]
   set x2 [expr {$botrightXpx - $redLineIndentPx}]
   set y2 [expr {$botrightYpx - $redLineIndentPx}]

   ## On the arc labelled from 0 to 100 (percent),
   ## with tic marks every 10 percent, draw the
   ## red-arc through 10 or 15 percent 
   ## at the top end of the 0 to 100 arc.

   # set extentDegrees $DEGperTIC
   set extentDegrees [expr {1.5 * $DEGperTIC}]

   $canvas create arc $x1 $y1 $x2 $y2 \
      -start -70 -extent $extentDegrees -style arc \
      -outline red -fill red -width 8

   ## FOR TESTING: (exit this proc before adding more to the meter)
   #   return


   ##################################
   ## Draw 4 rivets around the meter.
   ##################################

   set RIVETindentPx 10
   set RIVEToutdentPx [expr {$curCanvasSizePx - $RIVETindentPx}]

   ## upper-left rivet
   draw_rivet $canvas $RIVETindentPx $RIVETindentPx
   ## upper-right rivet
   draw_rivet $canvas $RIVEToutdentPx $RIVETindentPx
   ## lower-left rivet
   draw_rivet $canvas $RIVETindentPx $RIVEToutdentPx
   ## lower-right rivet
   draw_rivet $canvas $RIVEToutdentPx $RIVEToutdentPx

 
}
## END OF proc 'make_one_tachometer'


##+########################################################################
## PROC 'draw_rivet'
##+########################################################################
## PURPOSE: Put a rivet at a specified center point.
##          The center point is specified in pixels, as a location on
##          the canvas of the GUI, relative to the upper left corner.
##
##          (We pass the radius of the rivets in a global variable.)
##
## CALLED BY: the 'make_tachometer' proc
##+########################################################################

set rivetRadiusPx 4

proc draw_rivet { canvas centerXpx centerYpx } {

   global rivetRadiusPx

   ## FOR TESTING:
   #   return

   ########################################################
   ## Draw a color shaded arc using
   ## - 5 arc segments around each half of the circle/oval
   ## - 3 pixels for width of the arc segments
   ## - -45 degrees for the start angle (darkest shade)
   ########################################################

   draw_circle_shadow $canvas \
      [expr {$centerXpx - $rivetRadiusPx}] \
      [expr {$centerYpx - $rivetRadiusPx}] \
      [expr {$centerXpx + $rivetRadiusPx}] \
      [expr {$centerYpx + $rivetRadiusPx}] \
      5 3 -45.0

}
## END OF proc 'draw_rivet'


##+########################################################################
## PROC 'draw_circle_shadow'
##+########################################################################
## PURPOSE: Puts a shadowed edge around an oval/circle in a specified 'box'.
##          
## INPUTS: - the corner coordinates of the oval/circle box (in pixels)
##         - number of segments for the arc (segments of differing color shade)
##         - width (in pixels) to draw the arc segments
##         - start angle for drawing the arc segments
##
## CALLED BY: the 'make_tachometers' and 'draw_rivets' procs
##+########################################################################

proc draw_circle_shadow {canvas x1 y1 x2 y2 Nsegs ARCwidthPx startDEGREES } {

   ## FOR TESTING: (dummy out this proc)
   #   return

   set DEGperSHADE [expr {180.0/$Nsegs}]

   for {set i 0} {$i <= $Nsegs} {incr i} {

      set a [expr {($startDEGREES + $i * $DEGperSHADE)}]
      set b [expr {($startDEGREES - $i * $DEGperSHADE)}]
 
      ## Make darker grays for greater angles.
      set color255 [expr {40 + $i*(200/$Nsegs)}]
      set hexcolor [format "#%x%x%x" $color255 $color255 $color255]
 
      $canvas create arc \
         $x1 $y1 $x2 $y2 \
         -start $a -extent $DEGperSHADE \
         -style arc -outline $hexcolor -width $ARCwidthPx

      $canvas create arc \
         $x1 $y1 $x2 $y2 \
         -start $b -extent $DEGperSHADE \
         -style arc -outline $hexcolor -width $ARCwidthPx

      ## FOR TESTING:  (show each pair of arc segments before
      ##                drawing the next pair)
      # update

   }
   ## END OF loop over the arc segments

}
## END OF proc 'draw_circle_shadow'


##+########################################################################
## PROC 'update_needles'
##+########################################################################
## PURPOSE: Updates needle(s) on square canvas(es) --- using the
##          Linux/Unix/BSD/Mac 'df' command to get filesystem storage
##          space (%-used,allocated-Gig, and used-Gig).
##
##          This proc uses the 'update_one_needle' proc to update
##          the needle for each of the Nmeters canvases, where
##          each canvas corresponds to a file system.
##
## CALLED BY: the 'Additional GUI Initialization' section at the
##            bottom of this script.
##+########################################################################

proc update_needles {} {

   global Nmeters DIRscripts WAITseconds MOUNTname VARsampcnt

   ## FOR TESTING: (dummy out this proc)
   #   return

   ###############################################################
   ## For a given file-system mount-point-name, get tne 'df' data
   ## --- %-used, alloc-gig, used-gig, free-gig, device-name.
   ##
   ## Here is an example of the output of the '' script
   ## for a given mount name (directory name):
   ##   $ ./get_fileSystems_spaceData.sh /home
   ##   /home    46       75.973     32.542     39.572  /dev/sda5
   ##
   ## We use the Tcl 'foreach-break' 'trick' to put the
   ## space-separated values into separate variables.
   ###############################################################

   foreach {MOUNTname PERCENTused ALLOCgig USEDgig AVAILgig DEVICEname} \
      [exec $DIRscripts/get_fileSystems_spaceData.sh $MOUNTname]  {break}

   incr VARsampcnt

   ## FOR TESTING:
   if {0} {
      puts "proc 'update_needles': (parms returned from shell script)"
      puts "MOUNTname: $MOUNTname"
      puts "PERCENTused: $PERCENTused"
      puts "ALLOCgig: $ALLOCgig"
      puts "USEDgig: $USEDgig"
      puts "AVAILgig: $AVAILgig"
      puts "DEVICEname: $DEVICEname"
   }

   #########################################################
   ## Update the needles on up to Nmeters canvases.
   #########################################################

   # for {set i 1} {$i <= $Nmeters} {incr i} {
   #    eval update_one_needle .fRmeters.fRmeter$i \
   #       $aRallocated($i) $aRused($i) $aRavail($i) "$aRfilesystem($i)"
   # }

   update_one_needle .fRmeters.fRmeter1 \
      $ALLOCgig $USEDgig $AVAILgig "$MOUNTname"

   ############################################################
   ## Force the needles to show up on the GUI. (Not needed?)
   ############################################################

   # update

   ################################################################
   ## 'Pseudo-Recursively' 'fork off' another (delayed) instance of
   ## 'update_needles' here to support the wait-seconds scale widget
   ## --- using the 'after ms cmd arg arg ...' form of the 'after'
   ## command.
   ##
   ## We may need an 'after idle update_needles' somewhere
   ## in this script to 'register'/'queue' the 'update_needles'
   ## proc for execution at idle times --- to assure responsiveness
   ## of the GUI. (Not needed?)
   ################################################################

   set WAITmillisecs [expr {int($WAITseconds * 1000)}]
   after $WAITmillisecs update_needles

}
## END OF proc 'update_needles'


##+########################################################################
## PROC 'update_one_needle'
##+########################################################################
## PURPOSE: Updates the a needle on a square canvas --- using the canvas ID
##          and the allocated,used,filesys 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 needle as a proportion of the (square) canvas size.
##
## CALLED BY: the 'Additional GUI Initialization' section at the
##            bottom of this script, in a 'while' loop.
##+########################################################################
 
proc update_one_needle {frame TOT USED AVAIL FILESYS} {

   global pi radsPERdeg Nsegs

   ## FOR TESTING: (dummy out this routine)
   #  return

   ## Better to use AVAIL instead of USED to calculate the %-used.
   ## Reference:
   ## http://larsmichelsen.com/open-source/answer-why-does-df-k-show-wrong-percentage-usage/
   ## which references 'man mke2fs' --- explanation of '-m' parameter
   ## --- a reserved-blocks-percentage which is typically about 5%.

   ## So replaced this statement by the following.
   # set PERcent  [expr {($USED  * 100.0) /  $TOT}]

   set USED2 [format "%6.3f" [expr {$TOT - $AVAIL}]]
   set PERcent  [expr {($USED2  * 100.0) /  $TOT}]

   set TOTtext  "Total space for filesys '$FILESYS' = $TOT Gigabytes"
   set USEDtext "Used+Reserved in filesys '$FILESYS' = $USED2 Gigabytes"
   set AVAILtext "Available  in filesys '$FILESYS' = $AVAIL Gigabytes"

   $frame.labelINFO1 configure -text "$TOTtext"
   $frame.labelINFO2 configure -text "$USEDtext"
   $frame.labelINFO3 configure -text "$AVAILtext"


   ############################################################
   ## Convert PERcent to a needle-angle measured from 3 o'clock
   ## --- i.e. based at x-axis extending to the right (east).
   ############################################################
   ## Recall that the '-start' of Tk 'create arc' is at
   ## the 3 o'clock (east) position.
   ## We have the arc of 0 to 100 percent going from 250
   ## degrees to -70 degrees as we traverse the 'dial'
   ## clockwise --- for a total of 320 degrees.
   #########################################################

   ## Set the angle for the zero-point on the arc-of-tic-marks.

   set angle0  250.0

   set degs [expr {$angle0 - (320.0 * $PERcent / 100.0)}]
   set rads [expr {$degs * $radsPERdeg}]

   ## FOR TESTING:
   #    puts "proc 'update_one_needle'."
   #    puts "TOT: $TOT    USED: $USED    AVAIL: $AVAIL"
   #    puts "PERcent: $PERcent   degs: $degs  rads: $rads"

   ## Get the coord(s) of the center of the (square) canvas
   ## and calculate a length of the needle.

   # set widthPx  [$frame.can cget -width]
   set widthPx  [winfo width $frame.can]
   set halfPx   [expr {int($widthPx / 2.0)}]
   set lengthPx [expr {int($halfPx * 0.5)}]

   ## FOR TESTING:
   #    puts "proc 'update_one_needle'."
   #    puts "widthPx: $widthPx   halfPx: $halfPx  lengthPx: $lengthPx"

   ################################################################
   ## Calculate the coordinates for the tip and base of the needle.
   ################################################################
 
   set xtip [expr {$halfPx + $lengthPx*cos($rads)}]
   set ytip [expr {$halfPx - $lengthPx*sin($rads)}]

   set xbase $halfPx
   set ybase $halfPx
  
   ## Alternatively, we could draw the base of the needle
   ## a little bit away from the center of the canvas.
   # set xbase [expr {$halfPx + 0.2*$lengthPx*cos($rads)}]
   # set ybase [expr {$halfPx - 0.2*$lengthPx*sin($rads)}]


   ## Remove a previous needle, if any.

   catch {$frame.can delete -tags TAGneedle}

   ## Draw a red-line needle and a reddish-white line on either side
   ## --- for an (attempted) anti-aliasing effect.

   $frame.can create line \
      $xbase $ybase $xtip $ytip \
      -fill #ff0000 -width 4 -tag TAGneedle

   $frame.can create line \
      [expr {$xbase + 1}] [expr {$ybase + 1}] \
      [expr {$xtip + 1}]  [expr {$ytip + 1}] \
      -fill #ff8888 -width 2 -tag TAGneedle

   $frame.can create line \
      [expr {$xbase - 1}] [expr {$ybase - 1}] \
      [expr {$xtip - 1}]  [expr {$ytip - 1}] \
      -fill #ff8888 -width 2 -tag TAGneedle

}
## END OF proc 'update_one_needle'



##+#############################################################
## proc Refresh
##
## PURPOSE: 'Refresh' the one or more meters and their needles ---
##           for when the user wants a new set of values
##           and/or when the user resizes the window.
##
## CALLED BY: 'Refresh' button
##+#############################################################

proc Refresh {} {

   ## Cancel pending needle update(s), before redrawing
   ## the meter(s) and restarting the update_needles cycle.

   set LISTids [after info]
   foreach ID $LISTids {
      after cancel $ID
   }

   make_tachometers
   update_needles

}
## END OF proc 'Refresh'


##+#############################################################
## proc show_mounts
##
## PURPOSE: Shows available mount points (directories), in a
##          popup message window.
##
## CALLED BY: 'ShowMounts' button
##+#############################################################

proc show_mounts {} {

   global DIRscripts

   set MOUNTS [exec $DIRscripts/get_fileSystems_spaceData.sh mounts]

   popup_msgVarWithScroll .topMOUNTS "$MOUNTS"
}
## END OF proc 'show_mounts'


##+#############################################################
## proc Report
##
## PURPOSE: Shows a report on space-usage for all mount points,
##          in a popup message window.
##
## CALLED BY: 'Report' button
##+#############################################################

proc Report {} {

   global DIRscripts

   set USAGEtext [exec $DIRscripts/get_fileSystems_spaceData.sh all]

   popup_msgVarWithScroll .topREPORT "$USAGEtext"
}
## END OF proc 'Report'


##+#############################################################
## proc ReDraw_if_canvases_resized
##
## PURPOSE: To handle resizing the meter(s) when the window is
##          resized --- IF the <Configure> 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 .fRmeter.can <Configure> 
##            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} {

      make_tachometers
      update_needles

      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 'File System Space Usage' Monitoring Utility **

This utility is meant to show a GUI that holds a tachometer-style
METER, with a range of zero to one hundred percent.

The needle on the meter shows the PERCENT of available
FILE-SYSTEM STORAGE SPACE IN USE --- for a user-specifiable
file-system 'mount point' (directory name).

The meter is built and shown on a Tk 'canvas' widget.

Initially, the file system is set to the 'root' file system
--- directory name '/'. The 'ShowMounts' button can be used
to show other mount-points (directories) of other file-systems
(i.e. disk partitions) of this computer, if any.

For example, if '/home' is a mount point, that directory name
can be entered in an entry field to reset the file-system
for which the meter displays the PERCENT-USED.

The needle on the meter 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 the current
PERCENT-USED value for the user-selected mount point.
Then auto-updates continue at the current SampleRate setting.

Besides PERCENT-USED, the actual 'Allocated' and 'Used' space
values (in Gigabytes) are shown for the metered file system.

The user can click on a 'Report' button on the GUI to show
the allocated, used, percent-used values for the several file systems
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 'df'
command to get the file-system storage-space data --- for
the meter and for the 'Report' option.

***************************************
WINDOW RESIZE (an experimental feature):

The user may resize the window rather than using a fixed window
(and fixed meter) size. If the user resizes the window, the
'Refresh' button can be used to force the meter to be resized
according to the new window size. (The meter may be resized such that
it is 'too tall' for the new window size. Just pull the lower
window edge down, to see the entire meter.)

************************************
THE SCRIPT USED to update the meter(s):

A Tcl 'exec' command calls on a separate shell script ---
'get_fileSystems_spaceData.sh' --- that uses the 'df -kl' command
to get the 'Allocated' and 'Used' data for the 'local' file systems.
That shell script extracts-and-formats the data for return to this Tk script.

This shell script is used for 3 functions of this GUI:
  1) To show space-usage via the meter, for a user-specified mount point.
  2) To show the mount points known to this computer.
  3) To present a space-usage report for all the file systems
     known to this computer.

If the 'df' command is not available on your computer, then
you are probably not using a Linux, Unix, BSD, or Apple-Mac system.

For other operating systems, you may need to write a C program to get
file system 'allocated', 'used', and 'percent-used' 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 on its canvas, with 'make_tachometers'.
##  - Start an execution loop for the 'update_needles' proc.
##+################################################################

##+###################################################
## Set the scale widget var for initial 'refresh rate'
## (actually wait-time = 'wave-length', not 'frequency')
## --- in seconds.
## COMMENTED. There were technical problems with
## using a wait-time. The GUI became unusable during
## the wait. A different technique is needed.
##+###################################################

# set WAITseconds 60.0
set WAITseconds 120.0

## FOR TESTING:
# set WAITseconds 2.0
# set WAITseconds 4.0

 
##+#############################################################
## 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_needles', 'show_mounts', and 'Report'
## procs.
##+#############################################################

## FOR TESTING:
#  puts "argv0: $argv0"

# set DIRscripts "."
# set DIRscripts "[pwd]"
# set DIRscripts "$env(HOME)/apps/tkUtils"
  set DIRscripts "[file dirname $argv0]"


##+#################################################
## Draw the tachometer(s) --- without needle(s).
##+#################################################
## Need 'update' here to set the size of the canvases,
## because 'make_tachometers' uses 'winfo' to get
## the width and height of some frames and canvases.
##+#################################################

update
make_tachometers


##+#################################################
## Set a resize binding on the canvas ---
## to redraw the tachometer(s) and needle(s)
## if the window is resized.
##
## DE-ACTIVATED, for now.
## (Code is here for future experimentation.
##  When using the <Configure> event,
##  it is not easy to avoid extraneous redraws of
##  the GUI as the window is being dragged/resized.)
##+#################################################

if {0} {
set draw_wait0or1 0
set PREVcanvasesWidthPx  [winfo width  .fRmeters]
set PREVcanvasesHeightPx [winfo height .fRmeters]
bind .fRmeters <Configure> "ReDraw_if_canvases_resized"
}


##+#############################################################
## Initialize a couple of variables that are needed for the
## first call to the 'update_needles' proc. Variables:
## VARsampcnt and MOUNTname.
##+#############################################################

set VARsampcnt 0

## We start with the root file system as the file system
## whose %-used is shown on the meter.

set MOUNTname "/"

##+############################################
## Do an initial draw of the needle(s).
##+############################################

update
update_needles


Utility shell script (the 'wrapee')

Below is the code for the shell script called by this Tk script. This is a wrapper script for the 'df' command. This script takes a single string-parameter which can be either 'all' or 'mounts' or the name of a mount directory.

You can put this script in the same directory with the Tk script. The Tk script includes some code (involving the 'argv0' variable) to determine the location of the shell script by extracting the name of the directory in which the Tk script lies.

 Code for the shell script 'get_fileSystems_spaceData.sh' :
#!/bin/sh
##
## SCRIPT NAME: get_fileSystems_spaceData.sh
##
## PURPOSE:
##  This script gets 'allocated' and 'used' data (in Gigabytes) via output
##  from the 'df -k' command, where '-k' means the data is shown in 1K-blocks.
##
##  This shell script is meant to be capable of returning info of 3 types:
##      1) a report of the status of ALL the file systems known to 'df'
##      2) a list of the file-system mount-points
##      3) %-used, allocated-Gigabytes, used-Gigabytes for a user-specified
##         mount-point.
## 
## This script takes a single parameter --- a text string --- to indicate
## which of these 3 types of info to return:
##     1) 'all' 
##     2) 'mounts'
##     3) the name of a mount point such as '/' or '/home' or '/var',
##        if these are among the mount points returned by option-2.
##
## Example output from the 'df -k' command:
##
## Filesystem           1K-blocks      Used Available Use% Mounted on
## /dev/sda1             76896316   3468088  69522028   5% /
## udev                    507896       280    507616   1% /dev
## none                    507896       184    507712   1% /dev/shm
## none                    507896        80    507816   1% /var/run
## none                    507896         0    507896   0% /var/lock
## none                    507896         0    507896   0% /lib/init/rw
## /dev/sda5             75972964  31460380  40653372  44% /home
##
## 1)
## If 'all' is passed, all the data from the 'df -k' command is used,
## although it is reformatted into Gigabytes in the '1K-blocks', 'Used', and
## 'Available' columns. The data is SORTED by the PERCENT-USED column.
## The reformatted, sorted columnar data is returned by this script ---
## to stdout (standard-out).
##
## 2)
## If 'mounts' is passed, a list of the mount points is passed.
## For a computer with only a 'root' file system, then only '/' will be
## in the list that is returned (to stdout).
## For a computer with a separate filesystem mounted at '/home',
## then the two lines
##
##   /
##   /home
##
## will be in the list that is returned (to stdout).
##
## 3)
## If a mount point is passed (like '/' or '/home'),
## the %-used, allocated-Gigabytes, and used-Gigabytes data
## from the 'df" output line for that mount point is returned. Example:
## A line like
##
## /home    46       75.973     32.530     39.584  /dev/sda5
##
## may be returned to stdout, where the items are
## mount-point, %-used, Allocated-Gig, Used-Gig, Free-Gig, Filesys-device-name.
##
##+#########
## CALLED BY: a Tk GUI script that shows filesystem 'Allocated' and 'Used'
##            data as PERCENT-used needle readings on one or more meters
##           (dials) drawn on a Tk canvas --- Tk script name:
##                    meters_forFileSystems_usage.tk
##
## The 'all' output is intended to be used to show ALL the output
## from 'df -k' (reformatted) in a text-window of the Tk script GUI.
##
## The 'slash' output is intended to be used to position the
## needle of a tachometer-style needle of the Tk script GUI.
##
##+###################
## MAINTENANCE HISTORY:
## Updated by: Blaise Montandon 2013sep02 Started this script on Linux,
##                                        using Ubuntu 9.10 (2009 October,
##                                        'Karmic Koala').
## Updated by: Blaise Montandon 2013oct31 Added to comments and changed-fixed
##                                        some 'awk' parms.
## Updated by: Blaise Montandon 2013nov01 Added 'mounts' option. 
##+#########################################################################

## FOR TESTING: (show statements as they execute)
#  set -x

if test "$1" = "all"
then

   ##########################################################################
   ## SET REPORT HEADING.
   ##########################################################################

   HOST_ID="`hostname`"

   echo "\
......................... `date '+%Y %b %d  %a  %T%p %Z'` ........................

DISK USAGE (in Gigabytes) IN FILE SYSTEMS ON HOST *** $HOST_ID ***


           SORTED BY *PERCENT-USED* --- HIGHEST %-USED AT THE TOP
                      ************

******************
LOCAL FILE-SYSTEMS:
******************
                                                                    FileSystem
Directory                 ******  ALLOCATED  USED       AVAILABLE   Device-partition,
(Filesystem Mount Point)  % USED  Gigabytes  Gigabytes  Gigabytes   if any
------------------------- ------  ---------- ---------- ----------  ----------------
"


   ##########################################################################
   ## GENERATE THE REFORMATTED, SORTED DATA from the 'df -kl' command.
   ## ('l' for local file systems, not NFS-mounted).
   ##
   ## Note that the utilities 'sed', 'sort', and 'awk' are used.
   ##########################################################################

   ## FOR TESTING: (show statements as they execute)
   #  set -x

   df -kl | sed '1d;s/\%/ /g' | sort -n -r -k5 | awk \
   '{printf ("%-25s %6s %11.3f %10.3f %10.3f  %s \n\n", $6, $5, $2/1000000, $3/1000000, $4/1000000, $1)}'


   ##########################################################################
   ## ADD REPORT 'TRAILER'.
   ##########################################################################

   SCRIPT_BASENAME=`basename $0`
   SCRIPT_DIRNAME=`dirname $0`

   echo "
......................... `date '+%Y %b %d  %a  %T%p'` ........................

NOTE1: This file-systems report is SORTED by %-Used .... LARGEST %-Used FIRST.

       Hence the file-system on the first line may be the one of most
       immediate concern, if the %-Used is greater than 85%, say.

.............................................................................

   The output above is from script

$SCRIPT_BASENAME

   in directory

$SCRIPT_DIRNAME


   It ran the 'df' command on host  $HOST_ID .

   The script uses a 'pipe' of several commands (df, sed, sort, awk) like:

         df -kl | sed ... | sort -n -r -k5 |  awk '{printf ( ... ) }'

.............................................................................
NOTE2: The 'l' option of the 'df' command specifies that info is requested
       only for 'local' file systems.  This utility could be enhanced to
       also show 'df' data for 'nfs' mounted file systems.

NOTE3: This utility provides columnar formatting (in Gigabytes only) and sorting
       that is not available by use of the 'df' command by itself.
.............................................................................

......................... `date '+%Y %b %d  %a  %T%p %Z'` ........................
" 
   exit
fi
## END OF the if "$1" = "all" SECTION.


##+#############################################################
## THE FOLLOWING IS EXECUTED IF $1 CONTAINS 'mounts'.
##
## The output goes to standard-out.
##
## The following pipe of commands is used:
## 1) Uses 'grep' to get the lines that start with a slash (/)
##    in the first ('Filesystem') column.
## 2) Uses 'awk' to reformat the output --- retrieving only
##    column 6 (mount-point name) from each line.
## 3) Uses 'grep' to get the mount-point names that start with
##    slash (/).
##+############################################################

if test "$1" = "mounts"
then
   df -kl | grep '^/' | awk '{printf ("%s \n", $6)}' | grep '^/'
   exit
fi
## END OF the if "$1" = "mounts" SECTION.


##+#############################################################
## THE FOLLOWING IS EXECUTED IF $1 CONTAINS ANYTHING other than,
## 'all' or 'mounts'. It is assumed that $1 contains a mount-point
## name such as '/' or '/home'.
##
## The output goes to standard-out.
##
## The following pipe of commands is used:
## 1) Uses 'grep' to get the lines that start with a slash (/)
##    in the first ('Filesystem') column.
## 2) Uses 'grep' to get the line containing a string (mount-point name)
##    matching the string in $1.
## 3) Uses 'awk' to reformat the output --- changing kilobytes
##    to gigabytes, and re-ordering the columns to 6,5,2,3,4,1.
## 4) Uses 'sed' to change the '%' character to a space.
## 5) Uses 'head' to take the first line --- in case more than
##    one line is selected by the 2 grep's.
##+#############################################################

df -kl | grep '^/' | grep "$1" | awk \
   '{printf ("%s %6s %11.3f %10.3f %10.3f  %s \n", $6, $5, $2/1000000, $3/1000000, $4/1000000, $1)}' \
   | sed 's/\%/ /g' | head -1

## EXAMPLE OUTPUT FROM THE 'PIPE' ABOVE:
##
## /home    46       75.973     32.530     39.584  /dev/sda5
##


SIMILAR UTILITIES:

There is at least one more 'meter utility' on my 'to-do' list at the bottom of my 'bio' page at uniquename --- in the 'CME' (Code for MEters) group.

In particular, I may make a meter utility that shows CPU activity --- allowing for monitoring the activity of ANY of the CPU's on multiple-CPU computers, which are everywhere nowadays.


IN CONCLUSION

As I have said on several other code-donation pages on this wiki ...

There's a lot to like about a utility that is 'free freedom' --- that is, no-cost and open-source so that you can modify/enhance/fix it without having to wait for someone else to do it for you (which may be never).

A BIG THANK YOU to Ousterhout for starting Tcl-Tk, and a BIG THANK YOU to the Tcl-Tk developers and maintainers who have kept the simply MAH-velous 'wish' interpreter going.


uniquename UPDATE 2013nov24

In making a change to lengthen the red-line arc of the meter, I discovered that the %-used calculated from the 'Used' and total-allocated numbers of the 'df' command is less than the '%Used' reported by the 'df' command. A web search revealed that there is a 'reserve' of about 5% that is typically withheld in an 'ext' file-system for purposes described in 'man mke2fs' --- at the explanation of the '-m' parameter --- a reserved-blocks-percentage. Here is a web reference [2 ], which may go dead in a few years.

So I have changed the code of the Tk script above in several ways:

1) Changed the arc-size for the 'create arc' command of the 'make_one_tachometer' proc so that the red-line arc extends over 15 percent of the 'dial' (or 10 percent, in a commented statement) rather than about 5 percent.

2) Changed the 'update_one_needle' proc to calculate %-used (for positioning the needle on the meter) from the 'Available' number from 'df' rather than the 'Used' number from 'df'.

3) Touched up some comments --- mostly changed 'meters' to 'meter(s)'.

4) Added a 'bind' command for the 'MonitorMount' entry field --- to essentially press the 'Refresh' button for the user.

5) Added a '%' text item toward the bottom of the meter canvas. You could change the string to '% used' if that would be clearer.

The images shown above are thus changed to look like the following.

meter_filesysUsage_2013nov24Update_screenshot_298x395.jpg

The Tk script code above has been replaced by code with these changes.