TK GUIDE

This guide documents tkperl5alpha5 and is copyright (c) 1994 Malcolm Beattie, all rights reserved.

General layout of a tkperl program

        use Tk;
        $top = tkinit;        # default application name and display
        #
        # create lots of widgets
        #
        tkmainloop;
        #
        # Not reached here.
        # Subroutines can go below or at the top (or wherever you like).
        #
        sub acallback {
            # ...
        }
        sub anothercallback {
            # ...
        }
tkinit takes up to three arguments (all optional). The first is the application name which defaults to the basename of $0. The second is the display to use (defaults to DISPLAY from the environment...perl's magic environment in fact). The third is a sync flag which, if true, makes all communication with the X server synchronous (slow, but occasionally useful for debugging).

Widgets

Whereas widgets in Tk/Tcl are created by a Tcl command whose name is that of the widget class (e.g. button), in tkperl each widget class is a perl package (e.g. Button), with a method new. The new method takes as first argument either the parent widget object or else the Tk pathname you want to give the widget. [The return value of tkinit is a widget object referring to a toplevel widget]. Later arguments are optional and are pairs of initial configuration requests.

Example:

       Tk/Tcl      button .b -text foo
       tkperl      $b = Button::new($top, "-text" => "foo");
$b above is a reference to the pathname, blessed into the appropriate widget package. A previous command of something like

       $top = tkinit;
is assumed above and in other examples. In Tk/Tcl creation of a widget causes creation of a new Tcl command whose name is the widget's pathname and invoking this command with first argument foo invokes method foo on that widget. In tkperl, the widget's methods are real methods in the appropriate package and so one can use the blessed reference to invoke them.

Example:

       Tk/Tcl     .b flash
                  .b configure -textvariable bar
       tkperl     $b->flash();
                  $b->configure("-textvariable" => $bar);
There are a few minor differences in names: delete , index and select are perl keywords and have been renamed tkdelete, tkindex and tkselect. Tcl `submethods' such as .foo scan mark have been turned into single methods: scanmark, scandragto, selectclear, selectadjust, selectfrom, selectto. If the configure method of a widget is given one (extra) argument, it is taken to be an option name and a five element list consisting of (option name, database name, database class, default value, current value) is returned, unless the option name is a synonym in which case the two element list (option name, real option name) is returned. If no (extra) arguments are given to the configure method (e.g. C<@conf = $w->configure();>) then a list is returned consisting of a list ref for each option understood by the widget. Dereferencing each of those list refs gives a five or two element list as mentioned above.

Callbacks: slaves and methods

Configuration options where Tk/Tcl has a callback command (e.g. -command, -yscrollcommand) use slaves and methods instead in tkperl (-slave, -method, -yscrollslave, -yscrollmethod). Let's say you have a widget $w which you configure

        $w->configure("-slave" => $slave, "-method" => $method);
Callback behaviour depends on whether $slave is a blessed reference or just an ordinary value. If $slave is a blessed reference then the corresponding method is invoked in the package of the blessed reference and the first argument is $slave itself. In other words, if $method is "foo" then it's the same as doing

        $slave->foo();
in pure perl. If method is blank (some widgets have non-blank default method; most have blank default method) then no callback happens. On the other hand, if $slave is not a reference (in particular, if $slave is not defined), then $method is taken to be an ordinary subroutine and it is called with $slave as its first argument (or undef if $slave not defined). $method can be either the name of a subroutine ("foo" or "Somepackage::foo"), a subroutine reference such as \&foo or an anonymous subroutine reference such as

        sub { do_something; do_something_else }
In this last case, you not only get the ability to write callback code directly without having to name it but the code is also compiled before execution starts--the best of both worlds. In all cases, the callback routines are passed extra arguments in precisely the same situations as in Tk/Tcl (e.g. scrollbar callbacks).

Access to event fields

In Tk/Tcl event callbacks are specified as Tcl command strings in which magic cookies beginning % are replaced with data from fields from the event associated with calling the callback. For example, %W gets replaced by the pathname of the widget in which the event happened and %A gets replaced with the string from a keypress. In tkperl, no magic cookies are involved but the magic variables $Tk::EvW, $Tk::EvA and so on all refer dynamically to the appropriate data. One slight addition: since $Tk::EvW returns a pathname and sometimes blessed references are more useful, the subroutine &EvWref returns a blessed reference to the appropriate widget.

Variable tracking

In configuration options like -variable for radiobuttons where Tk/Tcl expects the name of a Tcl variable (as a string), tkperl allows a genuine perl variable.

Example

       Tk/Tcl     .r configure -variable foo -value green
       tkperl     $r->configure("-variable" => $foo, "-value" => "green");
Any alteration to that variable is tracked by the widget and it updates itself as necessary.

Binding events

Instead of Tk/Tcl bind, use tkperl's tkbind commands ( bind is already a perl keyword).

        tkbind($w, "<SomeEventName>", "somemethod");
$w can be a blessed reference to a widget or a widget pathname or a string corresponding to the class (e.g. "Button") or "all". The third argument is a method, as described in a previous section. An optional fourth argument is the slave/clientdata.

The packer

The packer command is tkpack (since pack is already a perl command). Widget arguments can be objects or pathnames, as usual. For example, if you have

        $w = Listbox::new($foo);
then you can pack it with

        tkpack $w;

Focussing

focus($w) takes a widget object or pathname as argument and sets the focus to that widget. With no arguments it returns a blessed ref to the current focus widget. focusdefault($w) does the same thing for the default focus.

Grabbing

grab($w) sets the grab to ref/pathname widget $w. If the optional second argument is true, the grab is global. grabcurrent does what Tk/Tcl grab current does (returning a list where necessary). grabrelease($w) does the equivalent of Tk/Tcl grab release. grabstatus ...guess what? All allow $w to be a pathname or blessed widget ref.

After: Callbacks after a time interval

after($ms, $method) arranges for $method to be called after $ms milliseconds. An optional third argument is the slave/clientdata. The form after($ms) causes a sleep of $ms milliseconds (with no event processing) before continuing.

Destroying widgets

destroy($w1, $w2, ...) destroys each widget in the list. Each can be a pathname or blessed widget ref.

Lowering and Raising widgets

lower($w, $below) lowers widget $w below widget $below. lower($w) lowers widget $w below everything. raise($w, $above) raises widget $w above widget $above. raise($w) raises widget $w above everything.

Waiting (and blocking) for things while events get processed

These are the tkperl versions of the Tk/Tcl tkwait commands. waitvar($var) waits until $var is assigned to. waitvis($w) waits until there is a change in visibility state (as indicated by the arrival of a VisibilityNotify event) in widget $w. waitwin($w) waits until widget $w is destroyed. In all three cases, event processing continues as normal.

Forcing processing of the event queue and when-idle tasks

The tkperl versions of the Tk/Tcl update command are as follows

       Tk/Tcl   update               (processes all events and when-idle tasks)
       tkperl   update;
       Tk/Tcl   update idletasks     (processes only when-idle tasks)
       tkperl   idletasks;

Processing a filehandle asynchronously

There is no Tk/Tcl equivalent for this, although Tk offers the service as C routine. The command addasyncio(FILEHANDLE, $selectfor, $method); (with an optional fourth argument, $slave) arranges for the method to be called whenever an interesting condition happens on the file descriptor FILEHANDLE. The interesting condition is chosen with the $selectfor argument which is a string containing one or more of the following characters: "r" $method is called when FILEHANDLE is readable "w" $method is called when FILEHANDLE is writable "e" $method is called when there is an exception on FILEHANDLE

When $method is called, it is called with the following arguments: $method($slave, $toread, $towrite, $exception); If $selectfor contained "r", then $toread is

        undef    if FILEHANDLE is not readable
        0        if FILEHANDLE is readable and at EOF
        n > 0    if FILEHANDLE is readable and `n' bytes can be read
                 without blocking from FILEHANDLE.
In the third case, you can use sysread(FILEHANDLE, $foo, $toread) to get those bytes into $foo. $toread is obtained by using the FIONREAD ioctl on FILEHANDLE. The program will die with an error if $method is called and this ioctl is not available.

If $selectfor contained "w", then $towrite is set to the result of the TIOCOUTQ ioctl. This value does not seem to be quite as obviously useful as that of FIONREAD in the read case, though.

If $selectfor contained "e", then $exception is true if and only if the select(2) indicated an exception on FILEHANDLE. The meaning of this is dependent on the flavour of Unix.

The "rwe" mask is set per file descriptor. Calling addasyncio again on the same file descriptor will replace the mask, $method and $slave with the new ones given. The command delasyncio(FILEHANDLE) removes the file handler created for FILEHANDLE. This may be necessary before closing the FILEHANDLE (a) to free up some internal memory and (b) to prevent the handler from getting called erroneously if a newly opened file reuses the underlying file descriptor. I can't piggy-back anything on perl's close() command, unfortunately, without changing perl itself.

Window manager commands

Use wm just as you would expect. For example,

       Tk/Tcl  wm maxsize . 4000 4000
       tkperl  wm("maxsize", $top, 4000, 4000);

Ringing the bell

To make the X bell beep use the command xbell(). It takes an optional volume argument, interpreted exactly the same as the Xlib routine XBell. Note that you have to flush the event queue after doing an xbell before the bell actually beeps--the update; command will do this.

Canvas widget

The postscript method of the canvas widget requires access to the Tk library directory to find prolog.ps. You will need to set the Tcl variable tk_library manually to this location before using the postscript method. For example, if your Tk library is Ftclcmd qw(set tk_library /usr/lib/X11/tk); anywhere before using the postscript method of a canvas widget.

Other Tk/Tcl commands

I haven't translated other Tk/Tcl commands to perl calling conventions yet, so you have to use the wrapper

        tclcmd("foo", "argument", "andanother", "etc");
Notice that none of this touches the Tcl parser. Make sure you pass each argument separately. Where the command expects a widget argument, you can always give it a widget object rather than a pathname and it will be automatically converted to the pathname that the underlying Tk command expects. The tclcmd command returns a string result like any Tcl command does so if it returns a Tcl list, you're on your own for parsing it.

Widget objects and inheritance

As alluded to above in a number of places, there are different ways of referring to widgets. You can use Tk pathnames, but you lose the advantages of perl objects and I may deprecate this in a future release. Return values of widget new methods are blessed references to a string (the new pathname, in fact), blessed into the appropriate widget package. When the first argument to a widget new method is a widget object (i.e. not a direct string pathname), an automatically generated unique pathname of a child of that widget is used instead (cf. Xt and Xaw). One way of constructing widget objects not yet referred to is very useful for simplifying widget inheritance for user-written composite widgets. A (blessed) reference to a hash is acceptable as a widget object. In this case, when a widget method is applied to it (e.g. via @ISA inheritance) it looks for a key of that widget's classname and takes the corresponding value. If the key isn't present it looks for the key Default. The value found is then decoded as a widget using all the above ways. For example, if I write

        package Scrolledlistbox;
        @ISA = (Listbox, Scrollbar);
        sub new {
            # create a new listbox $l
            # create a new scrollbar $s
            # link the two together
            bless {Listbox => $l, Scrollbar => $s};
        }
and then later have

        $sl = Scrolledlistbox::new($parent);
in package main, then $sl will understand the methods of both Listbox and Scrollbar and the methods will be applied to the appropriate widget ($l or $s). Certain tkperl commands look for special keys too (e.g. focus looks for Focus). You can also use the hash to cache any other necessary data for your new widget. For examples, see F

Writing .pl files for composite widgets

A couple of things to notice: a

        use Tk;
after your package declaration will ensure that all necessary subroutines are defined and imported into your package's namespace. If you need to postpone any initialisation until after tkinit has happened (class bindings for all widgets need to be delayed in this way) then you can push your package name onto @Tk::CLASSINIT. When tkinit happens and the toplevel widget is created the method classinit is invoked in each package in the list @Tk::CLASSINIT.

The Tkperldb.pm debugger

This is a tkperl version of Fpad for executing arbitrary perl commands which don't muck up the STDOUT screen. To use it, first set the PERL5DB environment variable to

        BEGIN { require Tkperldb; }
(making sure that perl knows where to find its library, either because perl5 is properly installed or by setting PERL5LIB yourself). Then use

        perl -d scripttodebug

Examples

Take a look in the F