Python GUIs: Summary and Conclusion

Mikael Lyngvig mikael at pobox.com
Wed Jun 30 19:51:16 EDT 1999


On Mon, 28 Jun 1999 18:09:48 GMT, "Michael P. Reilly"
<arcege at shore.net> wrote:

>I've already rewritten _tkinter to use a new pytcl module, but the
>functionality is still the same, ie. executing Tk calls from within an
>interpreter.  But I'd like to see it using the TK/C API (so much for
>my hard word on rewriting _tkinter *wink*).
>
>The main issue would be to make it extensible for adding the new
>widgets and other extensions (BTL, PIL, etc.).  Also, the interface
>between Python and C would not be as "simple" as the existing module.
>It's doable tho.
>
>Note that using the "native" Tk API would still require linking with
>the Tcl library.
>
>I could scratch my _tkinter rewrite and work on a native solution.
>(I could still publish my pytcl module.)  Any thoughts?

I've downloaded the Tcl 8.1.1 and Tk 8.1.1 sources, rebuilt them, and
studied them for a bit.

I think we're looking at a major task.  As far as I can determine from
the Tk sources, the parser is an integral part of the Tk library.
E.g., the Tk sources appear to be high-level down to the lowest level.

Even the lowest level functions takes argv[] style parameters and,
aassisted by a horde of strncmp()s (350+ in the generic directory),
parse the parameter/option list into a command, which is executed by
code which is interleaved with the parsing code.

I incorrectly assumed the design would be something like:

	[Tcl parser layer]
	[Tk parser layer]
	[Tk implementation layer]

But it appears to be:

	[Tcl parser layer]
	[Tk parser and implementation layer]

My hope was to make a Python interface run on top of the
implementation layer, but that appears to be impossible with the
current Tk design.

So, in order to make a native Tk layer for Python, we'd have to create
the implementation layer ourselves, and convince the Tk maintainers to
adopt this (if we'd want their future to be part of our TkInter
automatically).

I can illustrate what I mean by looking at the Tk bell command's
implementation:

int
Tk_BellObjCmd(clientData, interp, objc, objv)
    ClientData clientData;      /* Main window associated with
interpreter. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int objc;                   /* Number of arguments. */
    Tcl_Obj *CONST objv[];      /* Argument objects. */
{
    static char *bellOptions[] = {"-displayof", (char *) NULL};
    Tk_Window tkwin = (Tk_Window) clientData;
    char *displayName;
    int index;

    if ((objc != 1) && (objc != 3)) {
        Tcl_WrongNumArgs(interp, 1, objv, "?-displayof window?");
        return TCL_ERROR;
    }

    if (objc == 3) {
        if (Tcl_GetIndexFromObj(interp, objv[1], bellOptions,
"option", 0,
                &index) != TCL_OK) {
            return TCL_ERROR;
        }
        displayName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
        
        tkwin = Tk_NameToWindow(interp, displayName, tkwin);
        if (tkwin == NULL) {
            return TCL_ERROR;
        }
    }
    XBell(Tk_Display(tkwin), 0);
    XForceScreenSaver(Tk_Display(tkwin), ScreenSaverReset);
    XFlush(Tk_Display(tkwin));
    return TCL_OK;
}

The way I see it, this should have been something like this:

int Tki_WindowBell(Window)
   Tk_Window Window;
{
    XBell(Tk_Display(Window), 0);
    XForceScreenSaver(Tk_Display(Window), ScreenSaverReset);
    XFlush(Tk_Display(Window));
    return TCL_OK;
}

int
Tk_BellObjCmd(clientData, interp, objc, objv)
    ClientData clientData;      /* Main window associated with
interpreter. */
    Tcl_Interp *interp;         /* Current interpreter. */
    int objc;                   /* Number of arguments. */
    Tcl_Obj *CONST objv[];      /* Argument objects. */
{
    static char *bellOptions[] = {"-displayof", (char *) NULL};
    Tk_Window tkwin = (Tk_Window) clientData;
    char *displayName;
    int index;

    if ((objc != 1) && (objc != 3)) {
        Tcl_WrongNumArgs(interp, 1, objv, "?-displayof window?");
        return TCL_ERROR;
    }

    if (objc == 3) {
        if (Tcl_GetIndexFromObj(interp, objv[1], bellOptions,
"option", 0,
                &index) != TCL_OK) {
            return TCL_ERROR;
        }
        displayName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
        
        tkwin = Tk_NameToWindow(interp, displayName, tkwin);
        if (tkwin == NULL) {
            return TCL_ERROR;
        }
    }

    Tki_WindowBell(tkwin)
    return TCL_OK;
}

That way we could easily add a very fast Python layer on top of the
Tki_Xxx() functions.

So, in order to accomplish the task we'd have to restructure the
entire Tk source distribution - basically modify all functions in all
source files.

>We already know that Python 1.6 won't be until the new year, so any
>changes to Tkinter would be as a patch.

>From what I know now, I suspect that Python 3.0 could be done by the
time we're through :)

Also, if we did separate the parser and the implementation, we'd end
up with something which is directly incompatible with add-on widgets
and so on.  Although we could probably make a TkParser module, which
could interface to the parser layer.

My view, given the latest info, is that the task of making a native
TkInter is so huge that it doesn't pay off, unless your goal is to use
Tk in some form (not to get a fast, portable GUI).  The quickest way
to make TkInter faster is to wait a couple of years till your (then)
computer has quadrubled in speed.

What are your thoughts?  

P.S. Can anyone tell me how to ring a bell using the native Tk API?  I
don't see any way, except to do what Tk_BellObjCmd() does yourself.



-- Mikael





More information about the Python-list mailing list