1. EuGTK Convert Units
- Posted by Senator Aug 01, 2018
- 1624 views
Hello All,
I have been trying to write a EuGTK interface for Jim Roberts unit conversion library:
-- -- Measurement Conversion function (second update) -- (c2001) Jim Roberts sequence Unit integer l Unit = { -- Length {"m",1,"cm",100,"mm",1e3,"um",1e6,"km",1e-3,"in",1/.0254,"ft",1/.3048, "yd",1/.9144,"mi",1/1609.344,"fl",4.973e-3,"lg",2.071237307e-4, "au",6.684587154e-12,"ly",1.056980777e-16,"ps",3.24254e-17,"nmi",1/1852}, -- Time {"s",1,"min",1/60,"hr",1/3600,"dy",1/86400,"wk",1/604800,"yr",1/31557600}, -- Velocity {"m/s",1,"cm/s",100,"mm/s",1e3,"km/s",1e-3,"in/s",1/.0254,"ft/s",1/.3048, "mi/s",1/1609.344,"km/hr",3.6,"mi/hr",1/.44704,"kn",3.6/1.852}, -- Angles {"rad",1,"deg",57.29577951,"arcs",2.062648063e5,"arcmin",3437.746771, "gra",63.66197724}, -- Angular velocity {"rad/s",1,"deg/s",57.29577951,"deg/min",3437.746771,"rev/s",.1591549431, "rev/min",9.549296586,"rev/hr",572.9577952}, -- Area {"m2",1,"cm2",1e4,"mm2",1e6,"km2",1e-6,"in2",1550.0031,"ft2",10.76390841, "yd2",1.195990046,"mi2",3.861021585e-7,"acr",2.471053354e-04,"ha",.0001}, -- Volume {"m3",1,"cm3",1e6,"ft3",35.31466672,"in3",61023.74409,"yd3",1.307950618, "gal",264.1721,"lt",1e3,"pt",2113.3768,"qt",1056.6884,"mm3",1e9}, -- Weight {"kg",1,"g",1000,"lbs",2.205,"tn",1.1025e-3,"oz",35.28,"mtn",1e-3}, -- Pressure {"Pa",1,"N/m2",1,"bar",1e-5,"mbar",1e-2,"N/cm2",1e-4,"dn/cm2",10, "g/cm2",1/98.0665,"kg/cm2",1/98066.5,"atm",1/101325,"lbs/in2",1/6894.8, "lbs/ft2",1/47.88055555,"kg/m2",1/9.80665}, -- Energy {"j",1, "kj", 1000,"mj", 1000000, "erg",1e7,"btu",9.48e-04,"ft*lbs",.7376, "w*hr",2.778e-4,"kw*hr",2.778e-7, "kcal", 0.000239006} } l = length(Unit) global function convert_unit(atom val, sequence from, sequence To ) integer f, t, i1, i2 f = 0 t = 0 for a = 1 to l do -- indexes from and To units if not f then f = find(from, Unit[a]) i1=a end if if not t then t = find( To, Unit[a]) i2=a end if end for if not f or not t then return {not f, not t, 0} -- Error for unsupported units end if if i1 != i2 then return {-1, -1, 0} -- Error for mis-matched units end if val /= Unit[i1][f+1] -- converts val to base unit val *= Unit[i1][t+1] -- converts val to To unit return {0, 0, val} -- return no error and converted value end function -- additional routines added by Kenneth Rhodes -- index of categories global constant index = {"Length", "Time", "Velocity", "Angles", "Angular Velocity","Area", "Volume", "Weight", "Pressure", "Energy"} global object ul = { -- unit labels {}, -- sequence of "from" unit labels {} -- sequence of "to" unit labels } global constant Fr =1, To=2 -- sequence ul (unit labels) subsequence index -- used to select "from" and "to" unit labels --obtain sequence of category unit labels from index sequence global function SelectUnit(sequence category) integer i object cs ={} i = find(category, index) for a = 1 to length(Unit[i]) by 2 do cs = append(cs, Unit[i][a]) end for return cs end function -- test --sequence s = select_unit("Length") --for x = 1 to length(s) do -- printf(1, "%s\n", {s[x]}) --end for
I have encountered a number of problems. My plan was/is to use a ComboBox to display the sequence of unit categories from which I could select a specific category to use such as "length" or "area".
I envisioned a simple horizontal window layout with six "fields":
Category[index] [v1] [unit label 1] to [unit label 2] [v2]
"Category" is a label, [index] is a GtkComboBoxText displaying an index sequence of unit categories from which a specific category can be selected such as "length" or "area". The selected category then becomes "current" and is used as an index to select a sequence of unit labels pertinent to the current category from the master Unit sequence.
[v1] is a GtkEntry field where the user enters the value to be converted.
[unit label 1] is a GtkComboBoxText displaying the sequence list of unit labels pertinent to the selected category. From this list the unit label/type for the value v1 is selected.
"To" is a label
[unit label 2] is a GtkComboBox displaying the same list of unit labels, perhaps with label previously selected for unit label 1 removed. From this list The target "To" unit label/type is selected
[v2] is a calculated field displays the new value converted from v1.
Well, I can display and select from the index sequence and pass the selection to the two unit label combo boxes. I also can enter and retrieve a numeric value for V1. I cannot obtain a calculated converted value for V2.
My update routine does not work. I can select a new index category but it seems that my unit labels are stuck listing the unit labels for the initial listed category in the index sequence, "length".
- -- ConvertUnits.exw -- -- adapted from Irv's GtkComboBoxText Demo -- ----------------------------------------------------------------------------------- --# GtkComboBoxText selects from a limited set of choices. -- This is fine for most purposes, and easy to set up. See the GTk docs on how to -- use a GtkComboBox with a model for more complex uses. ----------------------------------------------------------------------------------- include GtkEngine.e include convert.e --constant docs = "<b><u>Convert Units</u></b>" constant docs = "Unit Catgory" constant win = create(GtkWindow,"border_width=10,position=1,$destroy=Quit"), panel = create(GtkBox,"orientation=HORIZONTAL,spacing=10"), lbl = create(GtkLabel), cb1 = create(GtkComboBoxText), -- index V1 = create(GtkEntry,"width-chars=8"), -- value to convert cb2 = create(GtkComboBoxText), -- "From" unit labels cb3 = create(GtkComboBoxText), -- "To" unit labels V2 = create(GtkEntry,"width-chars=8, %activate=ConvertUnit"), btnbox = create(GtkButtonBox,"spacing=5"), btn1 = create(GtkButton,"gtk-ok","Selection"), btn2 = create(GtkButton,"gtk-quit","Quit") ------------------------------------------------------------------ object ToV set(lbl,"text",docs) add(cb1,index) set(cb1,"active",1) -- see Note below add(V1, "text") ul[Fr] = SelectUnit(get(cb1, "active text")) add(cb2, ul[Fr]) ul[To] = SelectUnit(get(cb1, "active text")) add(cb3, ul[To]) add(V2, "text") add(win,panel) add(panel,lbl) add(panel,cb1) add(panel, V1) add(panel,cb2) add(panel,cb3) add(panel, V2) pack_end(panel,btnbox) add(btnbox,{btn1,btn2}) show_all(win) main() global function UpDate() atom x get(cb1, "text") get(V1, "text") set(V1, "active", 1) ul[Fr] = SelectUnit(get(cb1, "active text")) set(cb2, ul[Fr], "active", 1) ul[To] = SelectUnit(get(cb1, "active text")) set(cb3, ul[To], "active", 1) x = Convert_Unit() set(V2, x, 0) return 1 end function UpDate() global function Convert_Unit() UpDate() atom x x = get(V1, "text") x= to_number(x) ToV = convert_unit( x, sprintf("%s", {get(cb2, "active text")}), sprintf("%s", {get(cb3, "active text")})) -- convert_unit(x, get(cb2, "active text"), get(cb3, "active text")) x = ToV[3] set(V2, x) return x end function ------------------------------------------------------------- global function Selection() ------------------------------------------------------------- --UpDate() return Info(win,"Unit Conversion",{ get(cb1,"active text"), to_number(get(V1,"text")),get(cb2,"active text"), " to ", get(cb3,"active text"), " = ", get(V2, "text")}) end function -- Note: -- We set the first item to be 'active', which means it will -- be the one initially shown. Otherwise, nothing will be shown -- until the user clicks to drop down the list of items.
2. Re: EuGTK Convert Units
- Posted by irv Aug 01, 2018
- 1666 views
In the latest EuGTK 4.14.1 (posted July 31) there's an example of exactly that: see demos/examples/utility programs/convert.ex
However, I'll try to look at your code soon as I get home, and see if I can spot the problem.
3. Re: EuGTK Convert Units
- Posted by irv Aug 01, 2018
- 1647 views
- Last edited Aug 03, 2018
OK, I would change the added routine in convert.e to take an integer parameter for simplicty:
-- additional routines added by Kenneth Rhodes -- index of categories global constant index = {"Length", "Time", "Velocity", "Angles", "Angular Velocity","Area", "Volume", "Weight", "Pressure", "Energy"} --obtain sequence of category unit labels from index sequence global function SelectUnit(integer i) object cs = {} for a = 1 to length(Unit[i]) by 2 do cs = append(cs, Unit[i][a]) end for return cs end function
Then convert.ex might be:
include GtkEngine.e include convert.e constant docs = "Unit Catgory" constant win = create(GtkWindow,"border_width=10,position=1,$destroy=Quit"), panel = create(GtkBox,"orientation=HORIZONTAL,spacing=10"), lbl = create(GtkLabel,docs), cb1 = create(GtkComboBoxText), V1 = create(GtkEntry,"placeholder text=Value to convert"), cb2 = create(GtkComboBoxText,"$changed=Convert"), cb3 = create(GtkComboBoxText,"$changed=Convert"), V2 = create(GtkEntry,"placeholder text=Results"), btnbox = create(GtkButtonBox,"spacing=5"), btn1 = create(GtkButton,"gtk-ok","Convert"), btn2 = create(GtkButton,"gtk-quit","Quit") add(win,panel) add(panel,{lbl,cb1,V1,cb2,cb3,V2}) add(panel, V2) pack_end(panel,btnbox) add(btnbox,{btn1,btn2}) for i = 1 to length(index) do -- load first drop-down box with units; set(cb1,"append text",index[i]) end for connect(cb1,"changed","Update") show_all(win) main() --------------------------- global function Update() -- update both in and out units drop down lists; --------------------------- integer u = get(cb1,"active") object units = SelectUnit(u) set(cb2,"remove all") set(cb3,"remove all") for j = 1 to length(units) do set(cb2,"append text",units[j]) set(cb3,"append text",units[j]) end for set(cb2,"active",1) set(cb3,"active",1) return 1 end function ---------------------------- global function Convert() -- call Jim Roberts' convert routine in convert.e ---------------------------- atom val = to_number(get(V1,"text")) object from_units = get(cb2,"active text") object to_units = get(cb3,"active text") if val = 0 then return 1 end if if atom(from_units) or atom(to_units) then return 1 end if object results = convert_unit(val,from_units, to_units) results[1] = from_units results[2] = to_units results = append(results,val) set(V2,"tooltip text",text:format("[4] [1] = [3] [2]",results)) set(V2,"text",text:format("[4] [1] = [3:,,.4] [2]",results)) return 1 end function
Note1: there's no need to check for invalid units, as there are no invalid options presented
Note2: your layout looks better than the one in my demos.
4. Re: EuGTK Convert Units
- Posted by Senator Aug 03, 2018
- 1579 views
Thanks again Irv - for your kind comments and wonderful code demonstrations.
Regarding text:format, in the code segment:
results[3] = sprintf("%4g",results[3]) -- may need to change this; results = append(results,val) set(V2,"text",text:format("[4] [1] = [3:,,.2] [2]",results))
It is not clear to me how [3:.2] works in conjunction with format specifiers . I gather it is displaying results[3], but I was under the impression that the .2 meant the value would be displayed to two decimal places. It displays results[3] to 4 decimal places. This truncated the display when I used the program to convert 1 ft/lb2 to N/m2. The full display can be read by using the mouse to select the the result display and "swiping right". Changing %4g to %.2f renders a result value display to two decimal points.
Minor point I suppose, since the mouse can be used to display larger values and It is easy enough to assign a tool-tip to the result display advising the user to "swipe" right.
Can the Result display be lengthened?
Thanks again for developing EuGTK and sharing it with us.
Regards,
Ken
Oh, in the line
if v = 0 then return 1 end if -- v should be val
5. Re: EuGTK Convert Units
- Posted by irv Aug 03, 2018
- 1518 views
Good catch, I was accidentally cutting and pasting from two versions of the program from different windows, so some things got mixed up.
To widen the results, just add "width chars=40" (or some number) to the V2 create line.
I've edited the code in the post above so as not to confuse anyone reading this later.
Without formatting, I get: 1 m = 39.3700787401575 in
With the format "[4] [1] = [3:,,.4] [2]", I get: 1 m = 39.3700 in which seems to be reasonable precision.
However, 1 yd2 = 8.9999 ft2 seems to be not quite correct :) That's why "some experimenting" may be in order.
6. Re: EuGTK Convert Units
- Posted by irv Aug 03, 2018
- 1534 views
Replacing the function with the one below seems to give good precision, with the full "precision" shown in the tooltip (with the actual values shown in a terminal if run from one).
---------------------------- global function Convert() -- ---------------------------- atom val = to_number(get(V1,"text")) object from_units = get(cb2,"active text") object to_units = get(cb3,"active text") if val = 0 then return 1 end if if atom(from_units) or atom(to_units) then return 1 end if object results = convert_unit(val,from_units, to_units) results[1] = from_units results[2] = to_units display(results) set(V2,"tooltip markup",text:format("[4] [1] = [3] [2]",results)) results[3] = sprintf("%.4g",results[3]) display(results) results = append(results,val) set(V2,"text",text:format("[4] [1] = [3] [2]",results)) return 1 end function
7. Re: EuGTK Convert Units
- Posted by Senator Aug 05, 2018
- 1473 views
Marvelous! We can have context sensitive tool tips I hadn't realized how flexible the can be.
For what it is worth, I was able to eliminate the need for the "OK" button by adding the argument "$changed=Convert" to the GtkEntry for the value to convert and the ComboBox's for input and output units. Any change in any value causes the result box to update without automatically. So, I deleted the "OK" button.
I think I may have mentioned before that with some of your demo programs will generate "out of scope" error when translated & compiled on my system. IN the case of your convert.ex program a box pops up with the title "Warning" and displays the message:
function Update is not in scope make it global or link via _("Update)
This doesn't make since because the function "Update" is declared as global. I have no idea how to implement the link instruction. The executable file will run, but will display the warning/error message each time. The program code executes perfectly without error messages when run from the interpreter.
In your rewrite of my clumsy convert unit code the Update function did not require the argument "ctl" and the program compiles and runs with no error message. So is the problem with the version GTK installed on my Ubuntu system? Or with the OpenEu translate/Compile Code?
This process has been very instructive. Now I think I am thinking about rendering a version of my Geodesic Dome program in EuGTK.
So, once again, many thanks, Irv!
Regards,
Ken Rhodes
8. Re: EuGTK Convert Units
- Posted by irv Aug 06, 2018
- 1438 views
You may have found a bug in GtkEngine.e.
I will try to reproduce the error, using the code above, both with and without a parameter to the Update function (any params listed can safely be ignored, if they aren't needed).
9. Re: EuGTK Convert Units
- Posted by irv Aug 06, 2018
- 1455 views
Good catch! There is a bug in GtkEngine.e. Around line 1398 in function connect()
if rid > 0 then -- named function is in scope;
should be:
if rid > -1 then -- named function is in scope;
When compiled, the convert function has a routine_id of 0, which I didn't realize was a valid routine_id.
(The line number is for latest EuGTK 4.14.1, location may vary in earlier versions.)
10. Re: EuGTK Convert Units
- Posted by Senator Aug 07, 2018
- 1361 views
Good catch! There is a bug in GtkEngine.e. Around line 1398 in function connect()
if rid > 0 then -- named function is in scope;
should be:
if rid > -1 then -- named function is in scope;
When compiled, the convert function has a routine_id of 0, which I didn't realize was a valid routine_id.
(The line number is for latest EuGTK 4.14.1, location may vary in earlier versions.)
Fantastic! Works like a charm.