1. Xt and the Athena Widgets
Athena Widgets
- this course is based on the Athena widgets
- this is the original widget set, and by current standards is quite primitive
- we are using this widget set for the following reasons:
- Athena is widely available - it is part of the standard X distribution and should be
available on any workstation that runs X
- It is fairly simple - this is about the simplest widget set, it is easy to learn
- Athena is efficient - all the widgets are quite simple and require a minimum amount of
CPU resources
Basic Xt Functions
- we will start by looking at some of the standard Xt functions that are required by all
Athena applications
- once we have seen a few of these functions we will look at a simple example
1) XtInitialize
- this function is called at the beginning of the program, it initializes the toolkit,
connects to the X server and does many other things
- there are other ways of initializing X and the toolkit, but this is the easiest way
- the declaration of XtInitialize is:
Widget XtInitialize(shell_name, application_class, options,
num_options, argc, argv)
String shell_name;
String application_name;
XrmOptionDescRec options[];
int num_options;
int *argc;
char *argv[];
- we won't use the options and num_options parameters, NULL and 0 should be used as their
values
- the value shell_name is ignored
- application class is the name that is used for the application, we will see how this is
used later - choose a unique name for this
- the argc and argv parameters come from the corresponding parameters to main, X parses
the parameters to all X programs looking for standard X parameters
- on return argc contains the number of non-X parameters and argv contain the actual non-X
parameters
- XtInitialize returns an applicationShell widget, this becomes the root of the widget
hierarchy of the application that you are writing
2) XtCreateManagedWidget
- the XtCreateManagedWidget function is used to create a new widget and have its parent
manage the newly created widget
- when a widget is managed the parent will assign it screen space within its own space,
and the widget will be displayed whenever the parent is displayed
- the declaration of XtCreateManagedWidget is:
Widget XtCreateManagedWidget(name, widget_class, parent,
args, num_args)
String name;
WidgetClass widget_class
Widget parent;
ArgList args;
int num_args;
- the value returned by XtCreateManagedWidget is the newly created widget
- the name parameter is a character string that will be the name of the newly created
widget, names should be unique within an application
- widget_class is the type of widget to be created, there are symbolic constants for all
the widget classes in a toolkit, these names are used for this parameter
- parent is the parent widget of the newly created widget, this widget must have been
previously created
- args is a list of resource values for the widget, when the widget is created resource
values can be passed through this parameter, or NULL can be used if no resources are
passed
- num_args - the number of entries in the args array
3) XtRealizeWidget
4) XtMainLoop
- the XtMainLoop procedure is called when the initial widgets for the program have been
realized and are ready for interaction
- once this procedure is called, the application program has lost control and control is
transferred to X
- XtMainLoop waits for an event to occur and when it does it sends the event to the widget
that should handle it, this is the interaction part of the program
- the declaration of this procedure is:
void XtMainLoop()
- note that this procedure never returns
5) XtAddCallback
- callback procedures are used for communications between widgets and the application,
whenever something important occurs in a widget callback procedures can be called
- a widget usually has several callback lists, each lists corresponds to a particular
situation, when this situation occurs all the procedures on the list are called
- the XtAddCallback procedure is used to add a procedure to a callback list, it doesn't
remove any of the existing procedures on the list
- the declaration of this procedure is:
void XtAddCallback(object, callback_name, callback,
client_data)
Widget object;
String callback_name;
XtCallbackProc callback;
XtPointer client_data
- the first parameter is the widget the callback is being added to, and callback_name is
the callback list that the callback will be added to
- the callback parameter is the name of the callback procedure
- the client_data parameter is a pointer to a data structure that will be passed to the
callback procedure when it is called
- a callback procedure is declared in the following way:
void callback_proc(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
- the first parameter is the widget that the callback was called from, the same callback
procedure could be used with more than one widget
- the second parameter is the client data that was passed when the callback procedure was
added to the callback list, this is from the call to XtAddCallback
- since the client_data parameter is a pointer, the callback procedure can modify the data
structure that it points to
- the same client_data structure can be shared by several callback procedures, it can be
used to pass information between these procedures, later we will see examples of how this
is used
- the third parameter is a pointer to a data structure that is provided by the widget, the
contents of this data structure depend on the widget and the reason for the callback
- for example in the case of a scrollbar, the call_data will contain the new position of
the thumb, the callback procedure can use this information to move the information that is
displayed in the corresponding display widget
Example Program
- the first example program puts a button on the screen, when the left mouse button is
pressed in this button the programs exits
- a Command widget is used for the button, the name of this widget is used as the label
for the button
- all Xt programs must include the <X11/StringDefs.h> and
<X11/Intrinsic.h> include files, in addition an include file for each widget
class used in the application must also be included
- in this example the Command widget is used so we must also include
<X11/Xaw/Command.h>
- the main program initializes the toolkit, creates the command widget, adds a callback to
the command widget, realizes the widget hierarchy and then calls XtMainLoop
- the callback procedure simply exits from the program
/*****************************************************
*
* simple.c
*
* A very simple X program that uses the Athena
* widgets
*
****************************************************/
#include < X11/StringDefs.h >
#include < X11/Intrinsic.h >
#include < X11/Xaw/Command.h >
main(argc,argv)
int argc;
char **argv; {
Widget toplevel;
Widget command;
void quit();
toplevel = XtInitialize(argv[0],"simple",NULL, 0,
&argc, argv);
command = XtCreateManagedWidget("press and die",
commandWidgetClass, toplevel, NULL, 0);
XtAddCallback(command,XtNcallback,quit, NULL);
XtRealizeWidget(toplevel);
XtMainLoop();
}
void quit(w,client,call)
Widget w;
XtPointer client;
XtPointer call; {
exit(0);
}
Setting Resource Values
- resources are used to specify all aspects of a widget that can be changed
- the case of the command widget, the widget label, its size, its position, the colour and
font of the text, etc can all be modified by setting resource values
- there are many ways of setting resource values, we will only look at one of them, this
technique works for most resource values, and is quite flexible
- a resource consists of a name and a value, the name is represented by a character
string, and the value is represented by some type that is implementation dependent
- usually the value representation is a 32 bit word, so integers and floats can be stored
directly in it, and for all other types of values a pointer must be used
- the technique that we will use largely avoids the representation issue
- the following structure is used to represent resources:
typedef "something" XtArgVal;
typedef struct {
String name;
XtArgVal value;
} Arg, *ArgList;
- "something" is the data structure that the implementation uses to represent
resources
- the name field is the name of the resource, a character string
- the value field is the value of the resource
- most Xt routines actually take arrays of Arg structures and not single resource
specifications
- the XtSetArg macro can be used to fill in the entries in this structure
- the XtSetArg macro behaves in the following way: XtSetArg(arg, name, value) Arg arg;
String name; XtArgVal value;
- arg is an entry in an Arg array, name is the name of the resource, and value is the
resource value
- for each widget the Quick Reference Guide gives a list of its resources, the type
of the resource value, and the resource's default value (some of these are wrong)
- the actual string from this table can be used, for example in the case of the label
resource the string "label" can be used as the resource name, spelling mistakes
are not errors, the resource is ignored
- a safer way to specify the resource name is to use a set of constants that the widget
defines in its include file
- there is a standard way to determine these constants, the letters XtN are added to the
front of the resource name
- for example, for the label resource the constanct XtNlabel can be used as the resource
name
- when we want to establish the resource values for a widget the following program
skeleton is used:
Arg args[10]
int n;
n = 0;
XtSetArg(args[n],XtNheight,200); n++;
XtSetArg(args[n],XtNwidth,200); n++;
XtSetArg(args[n],XtNlabel,"my label"); n++;
- the XtSetValues procedure can be used to add the resource values to the widget, the
declaration of this procedure is:
void XtSetValues(w, args, num_args)
Widget w;
ArgList args;
int num_args;
- w is the widget the resources are added to, args is the list of resource values, and
num_args is the number of entries in the list
- for the above example, we can add the resouces using the following procedure call
XtSetValues(w, args, n);
Example Program
- the next example program sets some of the resources in the command widget
- first the size of the widget is changed to be 200 pixel high and 200 pixels wide, this
is done by setting the height and width resources of the widget
- then the label of the widget is changed to "press and die", this is done by
setting the label resource for the widget
- now we can give the widget a reasonable name, like "command"
/*****************************************************
*
* simple.c
*
* A very simple X program that uses the Athena
* widgets
*
****************************************************/
#include < X11/StringDefs.h >
#include < X11/Intrinsic.h >
#include < X11/Xaw/Command.h >
main(argc,argv)
int argc;
char **argv; {
Widget toplevel;
Widget command;
void quit();
Arg wargs[10];
int n;
toplevel = XtInitialize(argv[0],"simple",NULL, 0,
&argc, argv);
command = XtCreateManagedWidget("command",
commandWidgetClass, toplevel, NULL, 0);
n = 0;
XtSetArg(wargs[n],XtNheight,200); n++;
XtSetArg(wargs[n],XtNwidth,200); n++;
XtSetArg(wargs[n],XtNlabel,"press and die"); n++;
XtSetValues(command,wargs,n);
XtAddCallback(command,XtNcallback,quit, NULL);
XtRealizeWidget(toplevel);
XtMainLoop();
}
void quit(w,client,call)
Widget w;
XtPointer client;
XtPointer call; {
exit(0);
}
Example Program
- this example program uses three widgets, two simple widgets and a composite widget
- a label widget is used to give a label to the user interface, in this case "Hello
World"
- the same command widget is used exit from the program and it is set up in the same way
as in the previous programs
- a box widget is used to contain the two simple widgets, they are placed in the box in
the order that they are created
- the box is set up to stack the child widgets vertically and put 10 pixels between the
widgets
/*****************************************************
*
* simple.c
*
* A very simple X program that uses the Athena
* widgets
*
****************************************************/
#include < X11/StringDefs.h >
#include < X11/Intrinsic.h >
#include < X11/Xaw/Box.h >
#include < X11/Xaw/Command.h >
#include < X11/Xaw/Label.h >
main(argc,argv)
int argc;
char **argv; {
Widget toplevel;
Widget box;
Widget command;
Widget label;
void quit();
Arg wargs[10];
int n;
toplevel = XtInitialize(argv[0],"simple",NULL, 0,
&argc, argv);
box = XtCreateManagedWidget("box",boxWidgetClass,
toplevel, NULL, 0);
n = 0;
XtSetArg(wargs[n],XtNorientation,XtorientVertical); n++;
XtSetArg(wargs[n],XtNvSpace,10); n++;
XtSetValues(box,wargs,n);
label = XtCreateManagedWidget("label",
labelWidgetClass, box, NULL, 0);
n = 0;
XtSetArg(wargs[n],XtNlabel,"Hello World"); n++;
XtSetValues(label,wargs,n);
command = XtCreateManagedWidget("command",
commandWidgetClass, box, NULL, 0);
n = 0;
XtSetArg(wargs[n],XtNlabel,"press and die"); n++;
XtSetValues(command,wargs,n);
XtAddCallback(command,XtNcallback,quit, NULL);
XtRealizeWidget(toplevel);
XtMainLoop();
}
void quit(w,client,call)
Widget w;
XtPointer client;
XtPointer call; {
exit(0);
}
Creating and Managing Widgets
- the XtCreateManagedWidget procedure really performs two tasks, it creates a widget and
then manages it under its parent
- when a parent only has a few widgets this is the easiest way to create the children, but
it can lead to inefficiency when a parent has a lot of children, or not all of them should
be visible at the same time
- when a widget is managed the parent must find some screen space for it, this means that
the parent itself may need to grow and other widgets may need to move
- if a parent has a lot of children, these operations are repeated each time a new widget
is created using XtCreateManagedWidget, in this case it would be more efficient to batch
all the manage requests and do the widget layout once
- managing and creating widgets can be done using the following procedures:
Widget XtCreateWidget(name, object_class, parent, args,
num_args)
String name;
WidgetClass object_class;
Widget parent;
ArgList args;
int num_args;
XtManageChild(child)
Widget child;
XtManageChildren(children,num_children)
WidgetList children
int num_children
- the XtCreateWidget procedure creates a new widget without managing it
- the XtManageChild procedure will make the named child managed, which will make it
visible
- the XtManageChildren procedure takes a list of children, all the widgets on this list
must have the same parent widget, and manages them, this operation saves time
- the WidgetList type is an array of widgets
- widgets can also be unmanaged, when this occurs the widget disappears from the screen,
and depending upon the parent its screen space can be reused by other widgets
- managing and unmanaging widgets can be used to control the widgets that are visible on
the screen, if you don't want a command to be active at a particular point in time you can
unmanage its widgets
- the widget unmanaging procedure have the following declarations
XtUnmanageChild(child)
Widget child;
XtUnmanageChildren(children,num_children)
WidgetList children;
int num_children;
Example Program
- the next example program shows how widgets can be created and managed separately, and
how the client data can be used in a callback procedure
- this example program creates an arbitrary number of buttons in a box widget
- a char array, called names, contains the names of the buttons and one command widget is
created for each entry in the array
- a parallel array, called values, contains an integer value for each entry in the names
array, this value is printed each time the corresponding command widget is selected
- the same callback procedure is used for all of the command widgets, when XtAddCallback
is called the appropriate entry in the values array is passed as the client data, note
that a pointer must be used for this
/*****************************************************
*
* simple.c
*
* A very simple X program that uses the Athena
* widgets
*
****************************************************/
#include < X11/StringDefs.h >
#include < X11/Intrinsic.h >
#include < X11/Xaw/Box.h >
#include < X11/Xaw/Command.h >
#include < stdio.h >
char *names[] = {
"button1",
"button2",
"button3",
"button4",
"button5",
0
};
int values[] = {
1,
2,
3,
4,
5
};
main(argc,argv)
int argc;
char **argv; {
Widget toplevel;
Widget box;
Widget command;
void quit();
void bprint();
Arg wargs[10];
int n;
Widget buttons[20];
int nbuttons;
toplevel = XtInitialize(argv[0],"simple",NULL, 0,
&argc, argv);
box = XtCreateManagedWidget("box",boxWidgetClass,
toplevel, NULL, 0);
n = 0;
XtSetArg(wargs[n],XtNorientation,XtorientVertical); n++;
XtSetArg(wargs[n],XtNvSpace,10); n++;
XtSetValues(box,wargs,n);
command = XtCreateManagedWidget("quit",
commandWidgetClass, box, NULL, 0);
XtAddCallback(command,XtNcallback,quit, NULL);
nbuttons = 0;
while(names[nbuttons] != 0) {
buttons[nbuttons] = XtCreateWidget(names[nbuttons],
commandWidgetClass, box, NULL, 0);
XtAddCallback(buttons[nbuttons],XtNcallback,bprint,
&values[nbuttons]);
nbuttons++;
}
XtManageChildren(buttons,nbuttons);
XtRealizeWidget(toplevel);
XtMainLoop();
}
void quit(w,client,call)
Widget w;
XtPointer client;
XtPointer call; {
exit(0);
}
bprint(w,client,call)
Widget w;
int *client;
XtPointer call; {
printf("button %d\n",*client);
}
Go to Introduction
Go to Index
Go to Simple Widgets