Otk Tutorial 2 - Fahrenheit to Celsius Converter

This is the second of several tutorials showing how to use Otk to make graphical user interfaces (GUI's) for C applications. It makes a complete working application for converting temperatures from Fahrenheight to Celsius and vice versa. The example takes you through the steps of making panels, adding text labels, form-boxes, buttons, and sub-windows to produce the complete application. You should be able to make other applications after reading this.

Let's start from the basic shell which produces a blank window:

 #include "otk_lib/otk_lib.c"
 
 main( int argc, char **argv )
 {
   OtkInitWindow( 400, 200, argc, argv );
   ...
   OtkMainLoop();
 }

First we add two panels to place our widgets on. The panels separate sections of the GUI into logical subdivisions. They are not strictly necessary, as shown in the first example. But they can help to produce clean looking layouts. Panel layouts can also be re-used in other GUI's with little modification. We'll divide this GUI into an upper and lower section by placing the panels to occupy the full width, 0 to 100% horizontally. The upper panel will span from 0 to 70% of the outer window vertically, while the lower panel will occupy the remaining 30% below 70%. We will catch the OtkWidget handles returned when we make the panels. We will need these when we add objects on top them. The following shows the declared widget variables and the OtkMakePanel calls.

 #include "otk_lib/otk_lib.c"
 
 main( int argc, char **argv )
 {
   OtkWidget panel1, panel2;

   OtkInitWindow( 400, 200, argc, argv );
   panel1 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 0, 100, 70 );
   panel2 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 70, 100, 30 );
   
   OtkMainLoop();
 }

We chose Otk_Raised panels. Other choices would be Otk_Flat and Otk_Recessed. We choose to make the panels be a light-gray, but you might experiment with other colors. The coordinates of the first panel place it's upper left corner at (0%,0%) of the outer window, which is the upper left corner of the outer window. Panel1's lower right corner is at (100%,70%), which is on the right side and 70% down the outer window. The second panel's upper left corner was placed at (0%,70%), which is 70% down on the left edge of the outer window. Panel2's size is 100% wide by 30% high. This places it's lower right corner at (100%,100%), which is the lower right corner of the outer window. Note that these panels we added to the OtkOuterWindow. All subsequent objects will be added to one or the other of the panels.

Next, we place textual labels on the left side of panel1. It is often handy to refer to the documentation to see the formal definition of an Otk function's parameters. OtkMakeTextLabel is defined briefly as:
      OtkMakeTextLabel( container, text, fontcolor, fontscale, fontweight, x, y )

 #include "otk_lib/otk_lib.c"
 
 main( int argc, char **argv )
 {
   OtkWidget panel1, panel2;

   OtkInitWindow( 400, 200, argc, argv );
   panel1 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 0, 100, 70 );
   OtkMakeTextLabel( panel1, "Fahrenheit", Otk_Black, 1.0, 1.0, 5, 25 );
   OtkMakeTextLabel( panel1, "Celsius", Otk_Black, 1.0, 1.0, 5, 55 );

   panel2 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 70, 100, 30 );
	   
   OtkMainLoop();
 }

Note that panel1 is given as the container window in the first argument. After the text string, the Otk_Black color was specified, with a size and boldness of 1.0 and 1.0 respectively. The x, or horizontal location, of the start of the text (the left side) was set to be 5% from the left of panel1. This places it slightly in from the left side of the window. The y, or vertical locations, were set as 25% and 55% respectively for the two labels, to place one below the other. Remember, the coordinates are now relative to panel1, not the outer window! (The bottom of panel1 is 100%).

If we compile what we have so far, we will notice that the text labels are too small. Setting their scale to 3.0 makes them tall enough, but they appear too stretched-out lengthwise. Temporarily setting the aspect ratio to about 0.45 compresses the text appropriately for the labels. The aspect ratio is restored to unity afterwards, as show below.

 #include "otk_lib/otk_lib.c"
 
 main( int argc, char **argv )
 {
   OtkWidget panel1, panel2;

   OtkInitWindow( 400, 200, argc, argv );
   panel1 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 0, 100, 70 );
   Otk_Set_Text_Aspect( 0.45 );
   OtkMakeTextLabel( panel1, "Fahrenheit", Otk_Black, 3.0, 1.0, 5, 25 );
   OtkMakeTextLabel( panel1, "Celsius", Otk_Black, 3.0, 1.0, 5, 55 );
   Otk_Set_Text_Aspect( 1.0 );

   panel2 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 70, 100, 30 );
	   
   OtkMainLoop();
 }

Next, we add the text form-boxes, also called fill-in boxes. These provide places for the user to enter numbers or text. OtkMakeTextFormBox is defined as:
      OtkMakeTextFormBox( container, text, ncols, x, y, hsize, vsize, callback, param )

 #include "otk_lib/otk_lib.c"
 OtkWidget formbox1, formbox2;

 main( int argc, char **argv )
 {
   OtkWidget panel1, panel2;

   OtkInitWindow( 400, 200, argc, argv );
   panel1 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 0, 100, 70 );
   Otk_Set_Text_Aspect( 0.45 );
   OtkMakeTextLabel( panel1, "Fahrenheit", Otk_Black, 3.0, 1.0, 5, 25 );
   OtkMakeTextLabel( panel1, "Celsius", Otk_Black, 3.0, 1.0, 5, 55 );
   Otk_Set_Text_Aspect( 1.0 );
   formbox1 = OtkMakeTextFormBox( panel1, "", 12, 35, 20, 35, 25, 0, 0 );
   formbox2 = OtkMakeTextFormBox( panel1, "", 12, 35, 60, 35, 25, 0, 0 );

   panel2 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 70, 100, 30 );
	   
   OtkMainLoop();
 }

We made the formbox widget variables global so they can be easily referenced by callback routines. You are welcome to choose more original naming schemes. We left the initial text blank, and set the number of columns, or characters, to be contained at 12. The x-y locations of (35,20) and (35,60) place one below the other and just to the right of the text labels. The horizontal and vertical sizes are set to be 35% and 25% of the panel respectively. For now, the callback and callback-parameter were left as null (0). This could be used to call a specific subroutine and pass the parameter if the user presses enter (carriage-return) when typing in the box. We may fill these in later.

If we compile and run what we have so far, we notice the display would appear more balanced if the form boxes were moved slightly to the right. We therefore nudge the horizontal position of the form-boxes from 35 to 36, as shown next below.

Next we add two buttons to control conversion of temperature from Fahrenheit to Celsius or vice versa. OtkMakeButton is defined as:
      OtkMakeButton( container, x, y, hsize, vsize, text, callback, param )

 #include "otk_lib/otk_lib.c"
 OtkWidget formbox1, formbox2;

 void convert( void *sw )
 {
  char temp[100];
  float t1, t2;

  if ((int)sw==1)
   {
    Otk_Get_Text( formbox1, temp, 100 );
    if (sscanf(temp,"%f",&t1)!=1) {printf("error reading fahrneheit\n"); return;}
    t2 = (t1 - 32.0) * 5.0/9.0;
    sprintf(temp,"%g",t2);
    Otk_Modify_Text( formbox2, temp );
   }
  else
   {
    Otk_Get_Text( formbox2, temp, 100 );
    if (sscanf(temp,"%f",&t1)!=1) {printf("error reading celsius\n"); return;}
    t2 = t1 * 9.0/5.0 + 32.0;
    sprintf(temp,"%g",t2);
    Otk_Modify_Text( formbox1, temp );
   }
 }

 void cnvrt_frmbx( char *s, void *x ) { convert(x); }
 

 main( int argc, char **argv )
 {
   OtkWidget panel1, panel2;

   OtkInitWindow( 400, 200, argc, argv );
   panel1 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 0, 100, 70 );
   Otk_Set_Text_Aspect( 0.45 );
   OtkMakeTextLabel( panel1, "Fahrenheit", Otk_Black, 3.0, 1.0, 5, 25 );
   OtkMakeTextLabel( panel1, "Celsius", Otk_Black, 3.0, 1.0, 5, 65 );
   Otk_Set_Text_Aspect( 1.0 );
   formbox1 = OtkMakeTextFormBox( panel1, "", 12, 36, 20, 35, 25, cnvrt_frmbx, 1 );
   formbox2 = OtkMakeTextFormBox( panel1, "", 12, 36, 60, 35, 25, cnvrt_frmbx, 2 );
   OtkMakeButton( panel1, 75, 20, 20, 25, "Convert", convert, 1 );
   OtkMakeButton( panel1, 75, 60, 20, 25, "Convert", convert, 2 );

   panel2 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 70, 100, 30 );

   OtkMainLoop();
 }

Here we placed the buttons and added the conversion callback routines. The text appearing on the button will be scaled to fit the specified size of the button, regardless of the text's length. In general, if your text is very short, you may wish to either shorten the horizontal size of the button, or add spaces before and after your string to lengthen it to avoid having it appearing stretched. Conversely, if your text label is very long, you may consider making the button wider or shortening your text to avoid having the text appear squished.

Notice that we used a single callback routine for both buttons, convert, and used the callback parameter to distinguish the button pressed. Another way would have been to simply use two distinct callback routines. Notice that we also added the callback routines to the OtkMakeFormBox calls. The form-boxes have slightly different callback parameters, so the cnvrt_frmbx routine merely ignores the returned string and calls the convert routine.

How does the convert callback work? When called, it checks the input parameter to see which direction to convert. If Fahrenheiht-to-Celsius, it reads the text from the first formbox, converts it to float and writes the Celsius value to the second form-box. Otherwise it does the opposite.

Now we are finished the top panel. Let's complete this application by putting Help and Exit buttons on the lower panel. Below is show the new lines in the main section.

 main( int argc, char **argv )
 {
   OtkWidget panel1, panel2;

   OtkInitWindow( 400, 200, argc, argv );
   panel1 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 0, 100, 70 );
   Otk_Set_Text_Aspect( 0.45 );
   OtkMakeTextLabel( panel1, "Fahrenheit", Otk_Black, 3.0, 1.0, 5, 25 );
   OtkMakeTextLabel( panel1, "Celsius", Otk_Black, 3.0, 1.0, 5, 65 );
   Otk_Set_Text_Aspect( 1.0 );
   formbox1 = OtkMakeTextFormBox( panel1, "", 12, 36, 20, 35, 25, cnvrt_frmbx, (void *)1 );
   formbox2 = OtkMakeTextFormBox( panel1, "", 12, 36, 60, 35, 25, cnvrt_frmbx, (void *)2 );
   OtkMakeButton( panel1, 75, 20, 20, 25, "Convert", convert, (void *)1 );
   OtkMakeButton( panel1, 75, 60, 20, 25, "Convert", convert, (void *)2 );

   panel2 = OtkMakePanel( OtkOuterWindow, Otk_Raised, Otk_LightGray, 0, 70, 100, 30 );
   OtkMakeButton( panel2, 20, 20, 20, 60, "Help?", help, 0 );
   OtkMakeButton( panel2, 70, 20, 20, 60, " Exit ", quit, 0 );

   OtkMainLoop();
 }

The (void *) type-cast before the last parameter apeases the compiler to avoid warning messages. The call-back parameter of OtkMakeTextFormBox and OtkMakeButton is defined to pass pointers to structures. This enables many values to be passed if needed. However, when a single integer is to be passed, which is many times the case, then it's value can be passed in place of a pointer, and we tell the compiler we intend to do this by the type-casting.

Now we just need to write the Help and Exit call-back functions. The Exit call-back function, named quit, needs to simply call the C exit() function. The help call-back can open a sub-window and print a few lines of description with txt labels. Insert the following two call-back rountines just above the main routine.

...

void quit()
{ exit(0); }

void help()
{ 
 OtkWidget w;
 float y=15.0, dy=25.0;
	
 Otk_Set_Text_Aspect( 0.2 );
 w = OtkMakeWindow( Otk_Recessed, Otk_Blue, Otk_LightGray, 10, 10, 70, 80 );
 OtkMakeTextLabel( w, "This program converts temperature", Otk_Black, 3.0, 1.0, 5, y );
 y = y + dy;
 OtkMakeTextLabel( w, "in degrees Fahrenheit to degrees", Otk_Black, 3.0, 1.0, 5, y );
 y = y + dy;
 OtkMakeTextLabel( w, "Celsius, or vice versa.", Otk_Black, 3.0, 1.0, 5, y );
 Otk_Set_Text_Aspect( 1.0 );
}

main( int argc, char **argv )
{
 ...

To download this full example, right-click: degrees.c.

To compile: cc -I/usr/X11R6/include -L/usr/X11R6/lib degrees.c -lGLU -lGL -lXmu -lXext -lX11 -lm -o degrees.exe







Return to Tutorials page.

Back to main page

SourceForge.net Logo