Introduction
Programming for X can be cumbersome, in comparison tcl/tk provides
a quick and easy way of getting a graphical application up and running.
Tcl/tk is a scripting language, and as such there are some things
you just can't do, or can't do easily. The answer is to use a hybrid
of C and tcl/tk. The tcl/tk system comes with libraries which allow
a C program to call the tcl/tk interpreter and run tcl/tk scripts.
The provided library includes methods for initializing the environment,
calling different scripts, and accessing variables. Using this hybrid
environment also benefits the programmer by giving them access to
features which are inherent in X. Simple call back and timer functions
allow the programmer to schedule events, and the ability to register
a C function as a procedure in tcl/tk creates a powerful tool.
This document covers the basics of integrating tcl/tk scripts with
C. The Compile Options section describes the various libraries and
include files necessary to create a program. The Initialization
and Registering Commands section explains how to get started, and
how to call a C function from a tcl/tk script. The last section
Accessing Variables explains how to read and write tcl/tk variables
from functions in C.
Back to the top
--------------------------------------------------------------------------------
Compile Options
In order to access the tcl/tk library routines several things have
to be done to your source code and how you compile it. There are
two include files which have the declarations for the calls to the
library:
#include <tcl.h>
#include <tk.h>
Compiling for a hybrid application requires pointing the compiler
at the right include directories, the right libraries, and setting
the right link flags. On top of settings for tcl/tk it is also necessary
to include files and libraries for X. The following flags are those
to be set when using g++, your own system may vary depending on
compiler and file locations.
-I/software/tcl-7.4/include
-I/software/tk-4.0/include
-I/software/x11r5_dev/Include
-L/software/tcl-7.4/lib
-L/software/tk-4.0/lib
-L/software/x11r5_dev/lib
-ltk
-ltcl
-lX11
Back to the top
--------------------------------------------------------------------------------
Initialization and Registering Commands
Creating a hybrid tcl/tk & C application centres around a few
choice commands. The first of these is the "Tk_Main" function,
which is used to hand over control of the program to the tcl/tk
interpreter. This command does not return, so it should be the last
line in your "main" function, once all of your program's
initialization is done.
The "Tk_Main" function takes three parameters. The second
parameter is an array of strings, each string having a special meaning.
The first parameter is the number of indices in this array of strings.
The third parameter is a pointer to a function which is called for
initialization. This initialization function is where most of the
work gets done.
The array of strings which is passed to "Tk_Main" informs
the tcl/tk interpreter of the name of the application and the location
of the script which has the tcl/tk commands in it. This array is
actually the command line parameters which are passed to the wish-like
interpreter. The first item in the array gives the name of the application,
and the second is the location of the script to be run. If the script
is not in the same directory as the executable, it is wise to use
a fully qualified path.
Due to legacy reasons, tcl/tk requires that the strings passed
into most of its functions be modifiable, it also has the occasional
problem with function scope. The easiest way to avoid these problems
is to dynamically allocate the strings being passed in. The following
code fragment shows a call to "Tk_Main" using the application
"Hello World" and the script "hello.tcl".
// prototype for the initialization function
int InitProc( Tcl_Interp *interp );
// declare an array for two strings
char *ppszArg[2];
// allocate strings and set their contents
ppszArg[0] = (char *)malloc( sizeof( char ) * 12 );
ppszArg[1] = (char *)malloc( sizeof( char ) * 12 );
strcpy( ppszArg[0], "Hello World" );
strcpy( ppszArg[1], "./hello.tcl" );
// the following call does not return
Tk_Main( 2, ppszArg, InitProc );
Initialization Functions
The call to "Tk_Main" hands over control of your program
to the tcl/tk interpreter, but after some base initialization is
done and before the tcl/tk script is run a user defined function
can be executed. The above example shows a function of this type:
"InitProc". This user defined initialization function
must return an integer and takes one parameter Tcl_Interp *, a pointer
to an interpreter.
Inside of the initialization function is where you create an actual
interpreter with a call to "Tk_Init". The "Tk_Init"
function takes one parameter and that is a pointer to an interpreter,
this should just be the pointer passed into your initialization
function. The following code is a bare bones initialization function,
more will be added later.
int InitProc( Tcl_Interp *interp )
{
int iRet;
// Initialize tk first
iRet = Tk_Init( interp );
if( iRet != TCL_OK)
{
fprintf( stderr, "Unable to Initialize TK!\n" );
return( iRet );
} // end if
return( TCL_OK );
} // end InitProc
C Functions as tcl/tk procedures
By now you are familiar with procedure calls inside of tcl/tk script.
It is possible when programming a hybrid application to have a tcl/tk
procedure call a C function. To accomplish this requires a call
to the "Tcl_CreateCommand" function. This is normally
done inside of the initialization function. Calling the function
as a procedure in tcl/tk is just like calling any other procedure.
No declaration for the procedure should exist in the tcl/tk script.
Functions which are to be registered as procedures have a very
specific prototype. They must return an integer, and take four arguments.
The first argument is of type "ClientData" which is a
tcl/tk library type. The second argument is a pointer to the interpreter.
The last two arguments are similar to the "argc" and "argv"
arguments in a C "main" function. These two arguments
are used to pass the parameters given to the tcl/tk procedure. The
"argc" argument contains the number parameters passed
to the tcl/tk procedure, and the "argv" is an array of
strings, each string containing a parameter.
int Myfunc( ClientData Data, Tcl_Interp *pInterp, int argc, char
*argv[] );
When a function is registered to be used as a tcl/tk procedure it
can have a pointer associated with it, this pointer is passed in
as the "ClientData". The concept of "ClientData"
allows the programmer to associate a data structure with a tcl/tk
object, and calls to procedures can reference this object. This
structure is not often needed.
As was mentioned earlier the registration process requires a call
to the "Tcl_CreateCommand" function. This function takes
five arguments. The first argument is a pointer to an interpreter.
The second argument is a string which is the name of the tcl/tk
procedure. The third argument is a pointer to the function which
is called when the tcl/tk procedure is invoked. The last two arguments
are the "ClientData" item, and a pointer to a deletion
routine. The deletion routine allows a C function to be called when
the program exits in order to clean up structures associated with
objects. Like "ClientData" the pointer to the deletion
function is not often necessary. The following is a sample registration
of the tcl/tk procedure called "hey_there" which is to
call the above declared "Myfunc"
Tcl_CreateCommand( interp, "hey_there", Myfunc, (ClientData)NULL,
(Tcl_CmdDeleteProc *)NULL );
Back to the top
--------------------------------------------------------------------------------
Accessing Variables
Being able to call a C function by invoking a tcl/tk procedure allows
you to have tcl/tk get help from C. In order to have C get help
from tcl/tk there are a series of functions, the ones covered here
will deal with getting information from, and putting information
into a tcl/tk variable.
Tcl_GetVar
The "Tcl_GetVar" function returns a pointer to a string
which contains the contents of the specified tcl/tk variable. The
function takes three arguments, a pointer to the interpreter, a
string with the name of the tcl/tk variable, and a flag. The variable
that is accessed is at the current scope in the executing script
associated with the interpreter. If there is no variable at the
current scope level with the given name then global variables are
checked. If no matching global variable is found an error is returned.
The flags argument allows you to specify TCL_GLOBAL_ONLY, in order
to force the function to only check for global variables with the
given name. The following code is part of a tcl/tk script that will
be accessed.
set say_hello_to "World"
The following code is a the call in C to access the tcl/tk variable
"say_hello_to".
char sHelloTo[30];
// after this call sHelloTo should contain "World"
strncpy( sHelloTo, Tcl_GetVar( pInterp, "say_hello_to",
0 ), 29 );
Tcl_SetVar
The "Tcl_SetVar" function allows the programmer to change
the contents of a tcl/tk variable. The function takes four arguments,
the first is a pointer to the interpreter, the second a string indicating
the name of the tcl/tk variable to change, the third is a string
with the new value for the variable, and the last argument is for
flags. The "Tcl_SetVar" flags are the same as for "Tcl_GetVar".
The "Tcl_SetVar" function returns NULL if an error occurred
during the setting. If the variable does not exist, this function
will create the variable locally to the scope currently in execution
in the script referenced by the interpreter pointer. The following
code sets a tcl/tk variable called "say_hello_to" to contain
the value "World".
Tcl_SetVar( pInterp, "say_hello_to", "World",
0 );
TCL/TK 与 C 程序的集成
|