[-<--%--^-->-]
The hush library is intended to
provide a convenient way to program window-based applications in C++.
Basically, there are two considerations
that may lead you to employ
the hush library.
When you are familiar with Tcl/Tk and you need
to combine Tcl scripts with C++ code,
you may use handler classes to
do so in a relatively type-secure way.
On the other hand, when you want to program
graphical user interfaces in C++,
you may employ the hush widget
classes.
In the latter case, you may
choose to remain ignorant of
the underlying Tcl/Tk implementation
or exploit the Tcl script facility
to the extent you wish.
As an illustration of the structure of a program
using hush,
we will look at a simple program written in C++
that uses a graphical interface defined
by a Tcl/Tk script.
After discussing the example,
we will look at
a brief overview of the classes
that constitute the hush library.
A more detailed description will
be given of the kit class,
that encapsulates the embedded Tcl interpreter,
and the session class, that shields
off the details of the window environment.
Imagine that you have written
some numerical function,
for example a function employing the
Newton method for computing the square
root.
Such a function may be defined as in
the function newton:
When you have written such a function,
you may wish to have a graphical interface
to allow you to experiment with
possible inputs in a flexible way.
For example, you may wish to have a slider
for setting the input value and a message
widget displaying the outcome of the
function.
Such an interface may look like the one
in figure Interface.
Admittedly, the newton function
given above is simple enough to be implemented
directly in Tcl.
Nevertheless, since C++ is to be considered
superior for implementing numerical functions,
we decide to implement the Newton function
in C++ and the graphical interface in Tcl.
The problem we need to solve then is how to
connect the graphical interface with the C++
code.
The script defines a slider, as a (horizontal)
scale widget, and a message widget,
that is used to display the output.
The built-in Tcl/Tk bind function is used
to associate the movement of the slider
with the invocation of the Tcl function
generate.
Note that the function generate is a dummy function,
which merely echos the value of the scale widget
to the message widget.
Now we have developed a graphical interface,
which may be tested by using the hush
shell or wish.
Next, we need to develop the C++ program
embodying the numerical function and
connect it to the interface written
in Tcl.
The C++ code
The structure of this program is best explained
in a number steps.
Each of these steps corresponds with a code fragment.
Together, these fragments form the C++ program
shown below.
We will first look at the code.
Afterwards it will be explained why the individual fragments
are needed.
// Initial declarations
#include "hush.h"
double newton(double arg); // declare the function
char* ftoa( double f); // to convert float to char*
// The generator (handler) class gives access to the widgets
class generator : public handler {
public:
generator() { // access to Tcl widgets
s = (scale*) new widget(".s");
m = (message*) new widget(".m");
}
~generator() {
s->destroy(); m->destroy(); // to destroy widgets
delete s; delete m; // to reclaim resources
}
int operator()() { // the generator action
float f = s->get();
m->text( ftoa( newton(f) ) ); // display value
return OK;
}
private:
scale* s;
message* m;
};
// The application class takes care of installing the interface
class application : public session {
public:
application(int argc, char* argv[])
: session(argc,argv,"newton") {}
void main( ) { // tk is an instance variable
tk->source("interface.tcl"); // read interface script
handler* g = new generator();
tk->bind("generate",g); // bind Tcl command
}
};
// Finally, the function main is defined
void main (int argc, char **argv) {
session* s = new application(argc,argv);
s->run();
}
The functional part is represented by
the function newton.
We need to declare its type to
satisfy the compiler.
Further we need to include the hush.h
header file and declare
an auxiliary function ftoa
that is used to convert floating point
values to a string.
The next step
involves the definition of the
interfacing between the Tcl code
and the C++ program.
The class generator defines
a so-called handler object that will
be associated with the function
generate employed in the script,
overriding the dummy Tcl function generate
as defined in the script.
In order to access the scale and message widget
defined for the interface, C++ pointers to
these widgets are stored in instance
variables of the object.
These pointers are initialized when
creating a generator object.
The widgets
are destroyed when deleting the object.
Note that the widgets must first
be destroyed before deleting the corresponding C++
objects.
All you need to know at this stage is
that when the function generate
is called in response to moving
the slider,
or more precisely releasing the mouse
button,
then the operator() function of the
C++ generator object is called.
In other words, the operator()
function is (by convention)
the function that is executed
when a Tcl command that is bound
to a handler object is called.
The generator::operator() function
results in displaying
the outcome of the newton function,
applied to the value of the slider,
in the message widget.
Then we
define an application class,
which is needed for the program to
initialize the X-windows main event loop.
An application class must be
a subclass of the session class.
To initialize the program,
the application class redefines
the (virtual) function main
inherited from session.
The function application::main takes
care of initializing the interface,
creates an instance of the generator
class,
and binds the Tcl command generate
to the generator object.
Finally, the function main is defined.
A function main is required for each C or C++
program.
It consists merely
of creating an instance of the application class and the
invocation of run, which starts
the actual program.
Comments
The example C++ program illustrates
a number of features, some of which
are typical for hush
and some of which are due to programming
in a window environment.
In an ordinary C++ program the
function main is used to start the computation.
Control is effected by creating objects and
calling the appropriate functions.
When programming a window-based application, at a certain
moment control is delegated to the window environment.
Consequently, there needs to be some kind of main loop
which waits for incoming events, in response to which
the control may be delegated to an appropriate
component of the program.
To hide the details of activating the main loop
and the dispatching of events,
the hush library provides a
class session that allows you to
define an application class to initialize
your program.
In order to respond to events,
the hush library provides
a handler class, that allows you to associate a C++ object
with a Tcl function.
Each time the corresponding Tcl function
is invoked, the operator() function of the object
is called.
The actual object is an instance of a derived class,
redefining the virtual operator()
function of the handler class.
Handler classes are typical for hush.
Another feature typical for hush is the
use of a kit object,
that may be accessed by using the
tk instance variable of the
handler object.
The kit object provides access
to the Tcl interpreter embedded
in the C++ program.
In the example it is used to initialize
the graphical interface by reading a script file
and to define the association between
the Tcl function generate and
the C++ instance of generator.
The widgets defined in the Tcl script
are accessed in the C++ program
by means of
a scale and message pointer.
The hush library provides
for each Tk widget a class of the same
name.
Note that not the widgets
themselves are created in the constructor
of the generator class, but only
abstract widget objects that are
casted to the appropriate widget types.
Casts are needed to access these objects as respectively a scale
and message widget.
Widgets can be created, however, directly
in C++ as well, by employing the appropriate
widget class constructors.
See section User.
As a final comment,
the example illustrates a classical
stratagem of software engineering,
namely the separation of concerns.
On the one hand we have a script
defining the interface that may be
independently tested,
and on the other hand we have C++ code
embodying the real functionality of our program.
An overview of the hush class library
The example given in the previous section
showed what kind of components are
typically used when developing a program
with the hush library.
However, instead of employing a Tcl script,
the window interface may also be developed entirely
by employing hush C++ widgets.
In this section, a brief overview
will be given of the classes offered by
the hush library.
Further, it will be shown how to construct
the hush interpreter referred
to in the introduction.
In addition, we will take a closer look at
the classes kit and session,
which are needed to communicate with the embedded
Tcl interpreter and to initialize the main event loop,
respectively.
The hush C++ library consists
of three kinds of classes,
namely the widget classes
which mimic the functionality of Tk,
the handler classes,
which are involved in the
handling of events and the binding of C++
code to Tcl commands,
and
the classes kit and session,
which
encapsulate the
embedded interpreter
and the window management system,
In the widget class hierarchy depicted
in figure
Widgets, the class widget
represents an abstract widget,
defining the commands that are valid for
each of the descendant concrete
widget classes.
The widget class, however, is not an abstract class
in C++ terms.
As shown in the example in the previous
section,
the widget class allows for creating
pointers to widgets defined in Tcl.
In contrast, employing the constructor
of one of the concrete widget classes
results in actually creating a widget.
A more detailed example showing the functionality
offered by the widget classes will be given
in section User.
A description of the individual widget classes
is included in the appendix.
The handler class may also be considered
an abstract class, in the sense that
it is intended to be used as the ancestor of
a user-defined handler class.
Recall that in the example we defined
the generator class as a descendant
of handler.
The handler class has two
pre-defined descendant classes,
namely the widget class and
the class item.
This implies, indeed, that both
the widget and the item class
(that is treated in section Items)
may be used as ancestor handler classes as well.
The reason for this is that any descendant
of a widget or item
class may declare itself to
be its own handler and define the actions
that are invoked in response to
particular events.
This will be illustrated and discussed
in sections
Binding and
User.
The hush interpreter
In the introduction, hush
was announced as both a C++ library
and as an interpreter extending
the wish interpreter.
The program shown below
substantiates this claim,
albeit in a perhaps disappointingly simple way.
The structure of the program is similar
to the C++ example of section
Example.
In addition to including
the hush.h header file, however,
we must include the declarations needed for
employing the external hypertext (ht)
and distributed processing (dp) packages.
Next, we need to define an application class,
derived from session,
specifying how the hush interpreter
deals with command-line
arguments and what initialization must take place
before starting the main event loop.
At this stage it suffices
to know that the hush library
provides a hypertext widget and that
the -x option treats the next
argument as the name of a hypertext file.
In section
Hypertext,
an example will be given that
involves the hypertext widget.
Further, a predefined button .quit is packed to the root
widget.
The hush interpreter defined by the program
extends the wish interpreter
by loading the distributed processing
extension
and by allowing for the display of
a hypertext file.
The interpreter accepts any command-line
argument accepted by the wish
interpreter, in addition to the
-x hypertext option.
The Tcl interface script given
in section Script,
for example, may be executed using the
hush interpreter.
The kit class
Hush is meant to provide a simple C++
interface to Tcl/Tk.
Nevertheless, as with many a toolkit, some
kind of API shock seems to be unavoidable.
This is especially true for
the widget class (treated in
section Widget)
and the class kit
defining the C++ interface with the embedded Tcl interpreter.
The functionality of kit can only be completely
understood after reading this article.
However, since an instance of kit is used in almost
any other object (class), it is presented here first.
The reader will undoubtly gradually learn the
functionality of kit by studying the examples.
The class interface of kit is given
below:(
Each function listed in the class interface
is public unless it is explicitly
indicated as protected.
The interface descriptions start with the
pseudo-keyword interface.
This is merely done to avoid the explicit
indication of public
for both the ancestor and the member functions of the
class.
)
Each program written with hush
may contain only one embedded
hush interpreter.
To initialize the kit object wrapping
the interpreter and to start the main event loop,
an instance of the class session
must be created.
The preferred way of doing this is by defining
a descendant class of the session class,
redefining the virtual function
session::main to specify what needs
to be done before starting the main loop.
In addition, the constructor of the newly
defined class may be used to check
command line arguments and to initialize
application specific data,
as illustrated in the code for the
interpreter in section
Interpreter.