1. EuGTK Convert Units

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. 
 
 
 
new topic     » topic index » view message » categorize

2. Re: EuGTK Convert Units

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.

new topic     » goto parent     » topic index » view message » categorize

3. Re: EuGTK Convert Units

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.

new topic     » goto parent     » topic index » view message » categorize

4. Re: EuGTK Convert Units

Thanks again Irv - for your kind comments and wonderful code demonstrations. smile

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 
new topic     » goto parent     » topic index » view message » categorize

5. Re: EuGTK Convert Units

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.

new topic     » goto parent     » topic index » view message » categorize

6. Re: EuGTK Convert Units

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  
new topic     » goto parent     » topic index » view message » categorize

7. Re: EuGTK Convert Units

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. smile

So, once again, many thanks, Irv!

Regards,
Ken Rhodes

new topic     » goto parent     » topic index » view message » categorize

8. Re: EuGTK Convert Units

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).

new topic     » goto parent     » topic index » view message » categorize

9. Re: EuGTK Convert Units

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.)

new topic     » goto parent     » topic index » view message » categorize

10. Re: EuGTK Convert Units

irv said...

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. smile

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu