Version 0 of Adding Tk to an Existing Xt Program

Updated 2001-05-04 03:04:10

George Peter Staplin: This is a nice example for those of you that have an existing Xt program and want to add Tk. This could also be a solution for those of you that have existing custom Xt widgets, and want to use Tk widgets as well. I place the code below in the public domain. Use it however you want:


  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <X11/Intrinsic.h> 
  #include <X11/StringDefs.h> 
  #include <X11/Xaw/Command.h>  
  #include <X11/Xaw/Box.h>
  #include <X11/Xaw/Form.h>
  #include <X11/Xaw/AsciiText.h>
  #include <tcl.h>
  #include <tk.h>

  Tcl_Interp *interp;
  Display *dis;
  int screen;

  #define winIdLength 20

  void PressMe () {
  fprintf (stderr, "You pressed me.\n");
  }


  void Action () { 
  fprintf (stderr, "Now I'm going to exit.\n");
  exit (0); 
  }


  void addTkToplevelToXtBox (char *winName, Widget parent) {
  char *tCmd;

  tCmd = (char *) malloc (sizeof ("toplevel ") + sizeof (*winName) + sizeof (" -use ") + (sizeof (char) * winIdLength) + (sizeof (char) * 2));

  sprintf (tCmd, "%s%s%s%d", "toplevel ", winName, " -use ", XtWindow (parent));

        if (Tcl_Eval (interp, tCmd) != TCL_OK) {
        fprintf (stderr, "Error evaluating tCmd within addTkToplevelToXtBoxCmd() %s", Tcl_GetStringResult (interp));
        }
  free (tCmd);
  }


  Widget addXtBox (char *wClass, Widget wParent) {

        return (XtVaCreateManagedWidget (
                wClass,
                boxWidgetClass,
                wParent,
                NULL,
                0));
  }


  void makeXtBoxFitToplevel (Widget wid, char *winName) {
  Tk_Window twin = Tk_NameToWindow (interp, winName, Tk_MainWindow (interp));
  Tcl_Eval (interp, "update idletasks");
  XtResizeWidget (wid, Tk_Width (twin), Tk_Height (twin), 1);
  XFlush (dis);        
  }


  int main (int argc, char *argv[]) {
  Widget win, button;
  Widget testButton;
  Widget buttonShell;
  Widget xtBox2;
  Widget entry;
  int i = 1;
  XEvent event;
  XtAppContext appContext;
  char entryBuffer;


  Tcl_FindExecutable (argv[0]);
  interp = Tcl_CreateInterp ();

        if (Tcl_Init (interp) != TCL_OK) {
        fprintf (stderr, "Tcl_Init error"); 
        exit (1);
        }

        if (Tk_Init (interp) != TCL_OK) {
        fprintf (stderr, "Tk_Init error");
        exit (1);
        }


        win = XtVaAppInitialize(
                &appContext,          
                "mainWindow",           
                NULL, 
                0,                
                &argc, 
                argv,      
                NULL,
                0);   


  dis = XtDisplay (win);               
  screen = DefaultScreen (dis);

  buttonShell = addXtBox ("mainPanel", win);

        testButton = XtVaCreateManagedWidget (
                "Press Me",
                commandWidgetClass,
                buttonShell,
                NULL,
                0);

        button = XtVaCreateManagedWidget (
                "Exit",              
                commandWidgetClass,     
                buttonShell,               
                NULL,
                0);      


  xtBox2 = addXtBox ("xtBox2", buttonShell);

        entry = XtVaCreateManagedWidget (
                "entry",
                asciiTextWidgetClass, 
                buttonShell,
                XtNtype, XawAsciiString,
                XtNlength, sizeof (entryBuffer),
                XtNeditType, XawtextEdit,
                NULL,
                0) ;


  XtRealizeWidget (win);
  XtAddCallback (button, XtNcallback, Action, 0);
  XtAddCallback (testButton, XtNcallback, PressMe, 0);
  XtResizeWidget (win, 400, 400, 1);
  /*This MUST be here:*/
  XFlush (dis);



  addTkToplevelToXtBox (".t", xtBox2);

        if (Tcl_EvalFile (interp, "test_script") != TCL_OK) {
        fprintf (stderr, "Tcl_EvalFile error %s", Tcl_GetStringResult (interp));
        }

  makeXtBoxFitToplevel (xtBox2, ".t");


        {
        fd_set readfds;
        struct timeval timeout;
        int nfds;
        int tkFd = ConnectionNumber (Tk_Display (Tk_MainWindow (interp)));
        int xtFd = ConnectionNumber (XtDisplay (win));

                for (;;) {
                FD_ZERO (&readfds);
                FD_SET (tkFd, &readfds);
                FD_SET (xtFd, &readfds);
                timeout.tv_sec = 0;
                timeout.tv_usec = 900000;


                select (nfds, &readfds, NULL, NULL, &timeout);

                        while (XtAppPending (appContext) > 0) {
                        XtAppNextEvent (appContext, &event);
                        XtDispatchEvent (&event);
                        }

                        while (Tk_DoOneEvent (TCL_DONT_WAIT)) 
                        ;                        
                }
        }        
  return (0);
  }

The source code above invokes Tcl_EvalFile on test_script, which in this case looks like the code below:


  wm withdraw .

  pack [button .t.b -text Hello -command {puts Hello}]
  pack [listbox .t.l]
  pack [button .t.b2 -text {Can you see me?} -command {.t.l insert end Yes}] -fill x
  .t.l insert end {John Doe} {Jane Doe}

Download a working example here (Linux users add -ldl to the Makefile): http://www.xmission.com/~georgeps/TclTk_With_Xt4.tgz Note: You may need to change -ltcl83 and -ltk83 using a scheme like -ltcl8.3, depending on the name of your libtcl and libtk libraries.

It looks like this: http://www.xmission.com/~georgeps/TclTk_With_Xt.png