FE 'tkGooie' Utilities

---

'SELECTOR/CONVERTER-tools' group

---

tkDensityConvertSelect

(where densities may be kilograms-per-cubic-meter,
grams-per-cubic-centimeter, pounds-per-cubic-foot,
tons-per-cubic-lightyear, micrograms-per-liter,
ounces-per-gallon, grams-per-quart, grains-per-barrel
milligrams-per-deciliter, etc. --- huge or tiny)
(FE = Freedom Environment)

FE Home Page > FE Downloads Page > FE 'tkGooies' Description Page >

FE 'tkGooies' 'SELECTORtools' Page > This Page

INTRODUCTION to 'tkDensityConvertSelect'

I was planning (in 2016-2017) to make some Tcl-Tk GUI's for converting various kinds of quantities (distances, areas, volumes, weights, speeds, densities, etc.) from one specified unit-of-measure to another.

In August 2017, I published the first of these Tk GUI's --- a tkDistanceConvertSelect Tk script --- and soon after that a tkAreaConvertSelect Tk script and a tkVolumeConvertSelect Tk script and a tkWeightConvertSelect Tk script.

The converters for 'speeds' and 'densities' involve RATIOS --- not only ratios of numbers, but also ratios of units-of-measure --- distance-per-time for speeds --- and weight(or mass)-per-volume for densities.

This presents the issue of a long list of possible units-of-measure for speeds and densities. I handled that issue in making a tkSpeedConvertSelect Tk script. I used TWO listboxes of units, instead of the one units-listbox that I used in the previously written Tk 'converter' scripts.

One of my main goals in creating these 'convert' GUI's is to support conversions over a very wide range of values. For example, for distances, I wanted to be able to support converting distances that may be astronomically huge (for example, distances up to the diameter of the 'observable' universe, in light-years or meters or whatever) or distances that may be microscopically small (for example, distances at the sub-atomic level, in nanometers or meters or whatever).

To do this, I wanted to allow the user to specify the 'convert-from' distance in 'scientific notation' --- a decimal coefficient and a base-10 exponent (an integer).

And the 'convert-to' distance was also to be expressed in 'scientific notation'.

This is in contrast to the tkSpeedGun utility I wrote in 2013, which was intended to convert between kilometers-per-hour and miles-per-hour --- with the intention of handling a restricted range of speeds --- like those encountered by a tennis ball in a tennis match. That utility had the luxury of knowing that the speeds would be numbers in the range of zero to a maximum of about 300 (or 1000 or whatever) --- in increments of about 0.1.

Instead of dealing with a range of values FROM about 0.1 TO about 10-to-the-3rd, I wanted to deal with values in the RANGE of about 10-to-the-minus-80 TO about 10-to-the-plus-80 --- from 'nuclear-physics' sizes to 'astro-physics' sizes.

Using the tkSpeedConvertSelect Tk script as a starting point, I decided to code this 'density converter' utility --- and, like the distance, area, volume, weight, and speed 'converter' 'tkGooies' utilities, allow it to be a 'selector' utility also, in the sense that a specified density (in the specified units) could be written to 'stdout' --- by a click on a button of the GUI.


Weight versus Mass

DENSITY is a ratio of weight (or mass) over volume. WEIGHT is a quite variable quantity. It depends on the planet (or gravitational body) that one is considering and even the altitude relative to the center of that body.

MASS is independent of considerations like that. It may be helpful to think of the numerator of the density as being a mass rather than a weight. But our daily experiences involve weight rather than mass.

When one sees two different units of weight (pounds and kilograms, for example), it may help to think of two masses on a weight balance that are in balance on a particular planet (like Earth) and at a particular altitude (like sea level).

On one side of the balance, we have a collection of weights in pounds and fractions of a pound. On the other side of the balance, we have weights in kilograms and fractions of a kilogram.

Let us consider the collection of pound-weights as the given weight. In that case, this GUI can determine the collection of kilogram-weights that will balance the pound-weights. And these will balance no matter what planet we are on and no matter what altitude on that planet.

In other words, although we may think in terms of pounds being a weight (a force) and kilograms being a weight (a force), it may be helpful to think of dealing with masses rather than weights --- especially in extra-terrestrial situations.


Masses (or weights) and Volumes   (a huge number of combinations)

Density is a RATIO of mass(or weight)-over-volume. If we imagine allowing for 10 different units-of-measure for 'mass-or-weight' and 12 different units-of-measure for 'volume', this could result in 10 x 12 = 120 different units-of-measure of density to choose from.

Rather than using a single listbox with about 120 entries (or more), we use a 'mass-or-weight' listbox and a 'volume' listbox with about 10 to 20 entries each.

Examples of mass-or-weight units: pounds, kilograms, grams, ounces, tons, etc.

Examples of volume units (alphabetically): cubic-centimeters, cubic-inches, cubic-kilometers, cubic-meters, gallons, liters, quarts, etc.

Note: We allow for 'liquid' volumes as well as 'cubic' volumes.


Range of sizes   (huge and tiny densities)

This converter is intended to be able to convert densities chosen from a huge range of sizes: extremely high densities on the order of a ton per cubic-nanometer --- or much 'frothier', extremely low densities, on the order of a microgram per cubic-lightyear.

Estimates of the diameter of the 'observable' universe are on the order of 1.0 times 10 to the 11th lightyears --- which is about 10 to the 27th meters. This GUI allows for dealing with extremely low densities involving volumes as large as the volume of the known universe --- such as the density of a single hydrogen atom mass in the volume of the 'observable' universe.

A single kilogram mass in the volume of the 'observable' universe would result in an extremely low density on the order of 10 to the 3-times-minus-27 = -81 kilograms-per-cubic-meter.

Similarly, 'massive' exponents arise in considering extremely high densities.

Estimates of the diameter of a hydrogen atom are on the order of 1.0 times 10 to the -11 meters. And the size of a subatomic particle is at least 1000 times smaller --- so less than 10 to the -14 meters.

A single kilogram mass in the volume of a sub-atomic particle would result in an extremely high density on the order of 10 to the 3-times-plus-14 = 42 kilograms-per-cubic-meter.

This GUI can deal with extremely high densities such a ton in the estimated volume of a sub-atomic particle.

In order to be able to handle such huge ranges, in the various units, the GUI allows for specifying the density (in either of the pair of user-selected units) in 'scientific notation':

   a COEFFICIENT (for example, between 1.00000000 and 10000.00000000)
and
   an EXPONENT (an integer, assuming a 'base' of 10).

And the exponents we want to be able to handle are on the order of about minus-100 --- to plus-100 --- including zero. In other words, this GUI will allow for 3-digit exponents, at least --- and the exponent entry fields could be enlarged to handle even larger exponents.

Using scientific notation allows for dealing with densities over a huge range of magnitudes --- from a 'microgram/cubic-lightyear' (or less) to a 'megagram/cubic-micrometer' (or more) --- i.e. from the average density of the universe (or less) to the density of the Earth's core (or more) --- such as the density of the core of a 'neutron star' or a 'black hole'.


THE GUI DESIGN

Following a Tk GUI design procedure that I started using around 2015, I laid out a 'text-sketch' of a proposed layout for the GUI.

In the below sketch of the GUI:

  SQUARE BRACKETS indicate a comment (not to be placed on the GUI).
  BRACES          indicate a Tk 'button' widget.
  A COLON         indicates that the text before the colon is on a 'label' widget.

  UNDERSCORES     indicate a Tk 'entry' widget (if any).
  CAPITAL-O       indicates a Tk 'radiobutton' widget (if any).
  CAPITAL-X       indicates a Tk 'checkbutton' widget (if any).
  <----O---->     indicates a horizontal Tk 'scale' widget (if any).

The options available to the user are indicated by the following 'sketch' of the GUI:

 FRAMEnames
 VVVVVVVVVV
                    -----------------------------------------------------------------------------------
                    tkDensity - Converter/Selector
                    [window title]
                    -----------------------------------------------------------------------------------

 .fRbuttons         {UseIt} {Cancel} {Help} {Calc 2from1} {Clear2} {Calc 1from2} {Clear1}

 .fRmsg             [.... Messages to user are display in a label here ...............................]

 .fRleft                                                             .fRright
 [The sub-frames below are                                           [The 2 scrollable listboxes below
  in this '.fRleft' frame.]                                           are in sub-frames --- '.fRmass' and
                                                                      '.fRvolume' --- in this '.fRright' frame.]

                                                                     |----------------A |-------------------A
 .fRleft.fRunits1   Units1:[selected units displayed here] O units1  | ...            | | cubic-centimeters |
                    [in 2 text widgets separated by a 'per' label]   | ...            | | ...               |
                                                                     | ...            | | ...               |
 .fRleft.fRvalues1  Coefficient: ___________  Exponent: {+}___{-}    | grams          | | cubic-inches      |
                                                                     | ...            | | ...               |
 .fRleft.fRunits2   Units2:[selected units displayed here] O units2  | kilograms      | | cubic-kilometers  |
                    [in 2 text widgets separated by a 'per' label]   | ...            | | ...               |
                                                                     | ...            | | ...               |
 .fRleft.fRvalues2  Coefficient: ____________ Exponent: {+}___{-}    | ...            | | cubic-meters      |
                                                                     | pounds         | | ...               |
                                                                     | ...            | | ...               |
                                                                     | tons-US        | | ...               |
                                                                     | ...            | | ...               |
                                                                     | ...            | | cubic-yards       |
                                                                     |<-------------->V |<-------------->V


[Since the lists of units-of-measure for mass and volume could get rather long,
 we put the units-of-measure in the 2 listboxes in alphabetical order --- so that
 the user can rather easily locate a specific unit-of-measure.]

---

GUI components:

From the GUI 'sketch' above, it is seen that the GUI consists of about

   - 11 button widgets
   -  9 label widgets
   -  4 text widgets
   -  4 entry widgets
   -  2 listbox widgets (each with scrollbars)
   -  2 radiobutton widgets (1 pair on 2 separate 'units' lines of the GUI)
   -  0 checkbutton widgets
   -  0 scale widgets


For this density-convert GUI, I would need to use an 'edit_inputs' proc to check on decimal and integer values that a user puts in the 4 'entry' widgets. A slight modification of the 'edit_inputs' proc of the tkSpeedConvertSelect Tk script would suffice in this 'density convert' script.


SCREENSHOTS of the GUI

I set to work converting the 'tkSpeedConvertSelect' code to this proposed 'density-convert' GUI, and I ended up with the GUI seen in the following image.

This is the GUI as it appears on startup --- the 'Units2' coefficient entry widget is empty and the 'Units2' exponent entry widget is also empty.

'Units1' is initialized to 'kilograms' per 'cubic-centimeter' and 'Units2' is initialized to 'kilograms' per 'cubic-meter'. And the 'Units1' coefficient is initialized to 1.0 and the 'Units1' exponent is initialized to zero.

    These initial values can be easily changed by changing some 'set' statements at the bottom of the script, in the 'Additional GUI Initialization' section.

At this point, the user can simply click on the 'Calc 2from1' button to have the conversion of 1.0 'kilograms/cubic-centimeter' to 'kilometers/cubic-meter' performed --- with the result showing in the 'Units2' coefficient and exponen entry widgets.

In fact, for the following image, I did that. But the answer showed up as 1000000.00... for coefficient and zero for the exponent.

I decided to click on the '+' (plus) button on the right of that 'Units2' exponent until the coefficient went to 1.0 --- and the exponent increased to 6. Then I captured the following image.

---

Note that '-' and '+' buttons appear on either side of the 2 exponent entry fields.

After implementing the 'tkDistanceConvertSelect' GUI and trying it out, I found that it would be advisable to allow the user to adjust the 'scientific notation' to a combination of coefficient and exponent that would be most 'meaningful' (or 'readable') to the user.

So the four '+' and '-' buttons were added to the GUI --- along with proc's to adjust a coefficient and exponent pair for each click on one of the buttons.

Each click adjusts the exponent up or down one unit while moving the decimal point appropriately in the coefficient.

---

The message ('advice') area on the GUI lets the user know that the unit-of-measure for 'Units1' and 'Units2' can be changed by using the 2 radiobutton widgets on the GUI --- in conjunction with the TWO listboxes. After clicking on one of the 2 radiobuttons, the user selects a 'density' unit-of-measure by a click on a line in EITHER or BOTH of the two listboxes.

---

For the 'Selector' function of this utility:

If this converter-selector utility is embedded in another 'app', such as a shell script or another Tcl-Tk script, the 'UseIt' button can be clicked to send the two sets of units-coefficient-exponent to 'stdout' --- so that the calling script can use the entered data and/or the calculated data.

An example of how the 'stdout' text can be captured in a script variable is shown in comments near the top of the script.


The 'Help' button on the GUI provides rather complete and detailed help for using the GUI. A proc named 'popup_msgVarWithScroll' presents the Help text in a popup window.

Anyone who implements this script can easily change the Help text, which is defined in a 'set' statement for a 'HELPtext' variable, near the bottom of the script.


DESCRIPTION OF THE CODE

Below, I provide the Tk script code for this 'tkDensityConvertSelect' 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 from other scripts (code re-use).

I call your attention to step-zero. One new thing that I started doing around 2014 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.

All the 'set' statements for the text array, 'aRtext', are in one contiguous section toward the top of the code.


Experimenting with the GUI

In all my scripts that use the 'pack' geometry manager (which is all of my 100-plus Tk 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 rectangular widgets within it) as I resize the main window.

In this particular GUI, I have chosen to allow the window to be re-sized --- so that the TWO listboxes can be re-sized easily by 'pulling' on the lower-right corner of the GUI.

I also used '-fill x' and '-expand 1' for the two coefficient entry fields so that those fields will expand if the user x-expands the GUI window.

However, one could un-comment the statement

   wm resizable . 0 0

to make the window fixed at its initial size.

With the window resizable, 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.

---

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.


Some features in the code

There are plenty of comments in the code, 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.

Here is a quick view of how each of the procs are 'triggered'.

   'listboxM_select_units'   - called by a button1-release binding on the 'listboxM' widget.

   'listboxV_select_units'   - called by a button1-release binding on the 'listboxV' widget.

   'density_update'          - called by the two 'Calc' buttons.

   'adjust_values1'          - called by the '+' and '-' buttons for exponent1.
   'adjust_values2'          - called by the '+' and '-' buttons for exponent2.

   'clear_values2'           - called by the 'Clear2' button.
   'clear_values1'           - called by the 'Clear1' button.
 
   'edit_inputs'             - called by the 'density_update' proc.
   'decimal_check'           - called by the 'edit_inputs' proc.

   'put_vars'                - called by the 'UseIt' button.

   'advise_user'             - called in the 'Additional GUI Initialization' section
                               at the bottom of this script.  Could also be called by
                               some procs such as 'density_update'.

   'popup_msgVarWithScroll'  - called by the 'Help' button and by the 'edit_inputs' proc.


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 other equivalents) --- to surf the web to find out what President Trump is doing. Chances are he is on a golf course. Trump is spending much more time on the golf course while in office than Obama did while in office.

    On the campaign trail in 2016, Trump gave Obama a lot of flack for spending time on the golf course while in office. Then, when Trump gets in office, he demonstrates how to practically live on the golf course --- while running up a huge Secret Service security services bill and busting their annual budget in about 6 months.

    He's not draining the swamp, 'folks' --- he's flooding it. He seems to be an expert at busting budgets. He has the bankruptcies to prove it. He is building the most beautiful swamp you will ever see. Believe him, folks. ( Excuse me ... Excuse me, Donald. It's true.)

Hopefully the following code will help some people become Tcler's --- or become more productive Tcler's.


THE CODE

Here is a link to CODE for the script 'tkDensityConvertSelect.tk'.

To view this code, click on the link. To download it:

With your web browser, you can 'right-click' on this link --- and in the menu that pops up, select an item like 'Save Link Target As ...' --- to save this file to your local computer.

Then you can rename the file to remove the '.txt' suffix. Make sure that you have execute permission set on the file --- in order to execute the script.


Its place within the 'tkGooies' toolchest hierarchy

Here is a 2017 September image of the 'tkGooies' 'SELECTORtools' toolchest --- showing the 'Density Converter-Selector' drawer of the toolchest --- in a 'Huge/Tiny RATIO Converter-Selectors:' section of the toolchest.


SOME POTENTIAL ENHANCEMENTS:

Several possible enhancements for 'tkDensityConvertSelect' come to mind:

  • Format of the decimal (coefficient) output:
    Currently, the 'precision' (number of decimal places) displayed in the output is hard-coded in a variable named 'FORMATforCOEF' near the bottom of the script, in the 'Additonal GUI Initialization' section.

    A widget (such as a 'scale' widget --- or an 'entry' or 'spinbox' widget, to be more compact) could be added to the GUI --- to allow for specifying the number of decimal places to be displayed in a computed coefficient.

      A sort of 'homemade spinbox' widget was made in an RGB Color Array Generator Tk script. It uses 4 small '+' , '++' , '-' , and '--' buttons on either side of a widget that displays the current integer value. The '++' and '--' buttons allow for fast-incrementing of the integer.

    There are some loss-of-significant-digits issues that arise on a coefficient when clicking on a '+' exponent-button of the distance/area/volume/weight/speed/density convert 'tkGooies' a large number of times. Some of these problems may be handled by allowing the user to set the 'precision' of the two decimal coefficients.

  • More mass and volume units:
    Some other mass (or weight) and volume units could be added.

    Examples of additional mass (or weight) units:

    • kilotons
    • Jupiters
    • Earths
    • moons-of-Earth
    • suns-of-Earth
    • elephants
    • sperm-whales
    • 1957-Cadillacs

    Examples of additional volume units:

    • acre-feet-US-survey
    • bucket-US
    • cubic-cubit-ancient-Egypt
    • kiloliter
    • megaliter

    Each new unit-of-measure added would require adding 3 new setting statements near the bottom of the script, in the 'Additonal GUI Initialization' section. Example for mass (or weight):

        .fRright.fRmass.listboxM insert end "kilotons"
        set aRcoefM(kilotons/kilogram) [a decimal number goes here]
        set aRexpM(kilotons/kilogram) [an integer goes here]
        
  • Change the underlying conversion method:
    I have used the 'aRcoefM' and 'aRexpM' arrays to hold conversion factors based on using 'kilogram' for a common mass-or-weight-unit-of-measure to which all the other units-of-measure for mass-or-weight are 'tied'.

    And I have used the 'aRcoefV' and 'aRexpV' arrays to hold conversion factors based on using 'cubicmeters' for a common volume-unit-of-measure to which all the other units-of-measure for volume are 'tied'.

    There may be precision errors that are revealed in this approach --- depending on the accuracy of the coefficients used in the 'aRcoefM' and 'aRcoefV' arrays.

    For example, with the tkDistanceConvertSelect Tk script, converting 1 'mile' to 'feet' may yield a value like 5279.99993402 instead of 5280.

    Various techniques might be used to eliminate 'hiccups' like this. But ... since this utility does essentially all of what I had hoped that it would do, I prefer to go ahead and release the code rather than spending more time trying to deal with precision (or rounding) issues.

    NOTE:
    The conversion factors that I put in the 'aRcoefM', 'aRexpM', 'aRcoefV', and 'aRexpV' arrays may not be to the precision required to answer certain user questions. In fact, there may be some gross errors in one or more of these conversion factors. Testing should be done to determine if the accuracy of these factors is suitable for a particular application. The user of this code may have to change these factor values as needed. (Or some users may wish to change the technique --- in proc 'density_update' --- used to perform the conversions.)

  • Better ways of avoiding 'mismatched' values showing in the 4 entry fields:
    In this Tk GUI script, I have implemented a couple of ways to reduce the likelihood of the GUI displaying units1-coefficient-exponent values not equal to the units2-coefficient-exponent values ---

    • in the BINDINGS section of the code, and
    • in the 'listboxM_select_units' and 'listboxV_select_units' procs.

    In the BINDINGS section, the 4 entry fields have <KeyPress> bindings so that if a user is changing coef1 or exp1, then coef2 and exp2 are 'cleared' because the 2 densities (expressed in 2 different units-of-measure) are probably not going to be equivalent.

    And vice versa --- that is, if a user is changing coef2 or exp2, then coef1 and exp1 are 'cleared' because the 2 density triplets (units1-coef1-exp1 and units2-coef2-exp2) are probably not going to be equivalent.

    In the two 'listbox*_select_units' procs, coef1-and-exp1 is 'cleared' if the user changes 'units1' and 'coef2' is non-blank --- and coef2-and-exp2 is 'cleared' if the user is changing 'units2' and 'coef1' is non-blank. This is to reduce instances of the 2 density values not being equivalent --- due to a units-of-measure change.

    In addition, the 'Clear2' and 'Clear1' buttons allow the user to 'manually' clear coef2-and-exp2 and coef1-and-exp1, respectively.

    There might be better ways to achieve the goal here of eliminating the display of 'mismatched results'.

Since I have not used this script for a large number of tests over widely varying density magnitudes, there are probably some other enhancements that may be suggested by futher usage.

If I ever find that I am using this utility and I find that any of the features above are highly advisable to add or change (or implement in a different script), then I may return to this script to add/change/implement that feature.

Some additional ideas to include in this Tk GUI may be inspired by various on-line density converters on the internet, such as


IN CONCLUSION

As I have said on quite a few other Tcl-Tk code-donation pages on this site ...

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.

Bottom of the page for tkDensityConvertSelect --- a utility in the FE 'tkGooies' system, in the 'SELECTOR/CONVERTER-tools' group.

To return to a previously visited web page location, click on the
Back button of your web browser a sufficient number of times.
OR, use the History-list option of your web browser.
OR ...

< Go to Top of Page, above. >
< Go to FE 'tkGooies' 'SELECTORtools' Page >
< Go to FE 'tkGooies' Description page >
< Go to FE Home page. >

This FE web page was created 2017 Sep 01.
Page was changed 2017 Sep 24. (Added image of the SELECTORtools toolchest.)

If I ever post a copy of this code on the Tcler's Wiki site (wiki.tcl.tk) ---
as a backup and alternative to this page, I plan to put a link to that page here.