Tcl::Tk(3)

NAME
Tcl::Tk - Extension module for Perl giving access to Tk via the Tcl extension

SYNOPSIS
    use Tcl::Tk;
    my $int = new Tcl::Tk;
    my $mw = $int->mainwindow;
    my $lab = $mw->Label(-text => "Hello world")->pack;
    my $btn = $mw->Button(-text => "test", -command => sub {
        $lab->configure(-text=>"[". $lab->cget('-text')."]");
    })->pack;
    $int->MainLoop;

Or

    use Tcl::Tk;
    my $int = new Tcl::Tk;
    $int->Eval(<<'EOS');
    # pure-tcl code to create widgets (e.g. generated by some GUI builder)
    entry .e
    button .inc -text {increment by Perl}
    pack .e .inc
    EOS
    my $btn = $int->widget('.inc'); # get .inc button into play
    my $e = $int->widget('.e');     # get .e entry into play
    $e->configure(-textvariable=>\(my $var='aaa'));
    $btn->configure(-command=>sub{$var++});
    $int->MainLoop;

DESCRIPTION
The "Tcl::Tk" module provides access to the Tk library within Tcl/Tk installation. By using this module an interpreter object created, which then gain access to entire variety of installed Tcl libraries (Tk, Tix, BWidgets, BLT, etc) and existing features (for example natively looking widgets using "tile").

  Prerequisites
For full functionality you need the Tcl packages "snit", which is part of the standard tcl library (see <https://core.tcl.tk/tcllib>), and the standard Tk library (see <https://core.tcl.tk/tklib/home>).

Having correct installation of snit is much preferred. In case it isn't found - some predefined tcl/snit will be used.

  Access to the Tcl and Tcl::Tk extensions
To get access to the Tcl and Tcl::Tk extensions, put the command near the top of your program.

    use Tcl::Tk;

  Creating a Tcl interpreter for Tk
Before you start using widgets, an interpreter (at least one) should be created, which will manage all things in Tcl.

To create a Tcl interpreter initialised for Tk, use

    my $int = new Tcl::Tk;

Optionally DISPLAY argument could be specified: "my $int = new Tcl::Tk(":5");". This creates a Tcl interpreter object $int, and creates a main toplevel window. The window is created on display DISPLAY (defaulting to the display named in the DISPLAY environment variable)

The Tcl/Tk interpreter is created automatically by the call to "MainWindow" and "tkinit" methods, and main window object is returned in this case:

  use Tcl::Tk;
  my $mw = Tcl::Tk::MainWindow;
  my $int = $mw->interp;

  Entering the main event loop
The Perl method call

    $int->MainLoop;

on the Tcl::Tk interpreter object enters the Tk event loop. You can instead do "Tcl::Tk::MainLoop" or "Tcl::Tk->MainLoop" if you prefer. You can even do simply "MainLoop" if you import it from Tcl::Tk in the "use" statement.

  Creating and using widgets
Two different approaches are used to manipulate widgets (or, more commonly, to manipulate any Tcl objects behaving similarly)

•   access with a special widget accessing syntax of kind "$widget->method;"

•   random access with "Eval"

First way to manipulate widgets is identical to perl/Tk calling conventions, second one deploys Tcl syntax. Both ways are very interchangeable in that sense, a widget created with one way could be used by another way.

Usually Perl programs operate with Tcl/Tk via perl/Tk syntax, so user have no need to deal with Tcl language directly, only some basic understanding of widget is needed.

A possibility to use both approaches interchangeably gives an opportunity to use Tcl code created elsewhere (some WYSIWIG IDE or such).

In order to get better understanding on usage of Tcl/Tk widgets from within Perl, a bit of Tcl/Tk knowledge is needed, so we'll start from 2nd approach, with Tcl's Eval ("$int->Eval('...')") and then smoothly move to 1st, approach with perl/Tk syntax.

Tcl/Tk syntax

•   interpreter

    Tcl interpreter is used to process Tcl/Tk widgets; within "Tcl::Tk" you create it with "new", and, given any widget object, you can retrieve it by "$widget->interp" method. Within pure Tcl/Tk it is already exist.

•   widget path

    Widget path is a string starting with a dot and consisting of several names separated by dots. These names are widget names that comprise widget's hierarchy. As an example, if there exists a frame with a path ".fram" and you want to create a button on it and name it "butt" then you should specify name ".fram.butt". Widget paths are referred in miscellaneous widget operations, and geometry management is one of them.

    At any time widget's path could be retrieved with "$widget->path;" within "Tcl::Tk".

•   widget as Tcl/Tk command

    when widget is created, a special command is created within Tk, the name of this command is widget's path. That said, ".fr.b" is Tk's command and this command has subcommands, those will help manipulating widget. That is why "$int->Eval('.fr.b configure -text {new text}');" makes sense. Note that "$button->configure(-text=>'new text');" does exactly that, provided a fact $button corresponds to ".fr.b" widget.

"use Tcl::Tk;" not only creates "Tcl::Tk" package, but also it creates "Tcl::Tk::Widget" package, responsible for widgets. Each widget (object blessed to "Tcl::Tk::Widget", or other widgets in ISA-relationship) behaves in such a way that its method will result in calling its path on interpreter.

Perl/Tk syntax

"Tcl::Tk::Widget" package within "Tcl::Tk" module fully aware of perl/Tk widget syntax, which has long usage. This means that any "Tcl::Tk" widget has a number of methods like "Button", "Frame", "Text", "Canvas" and so on, and invoking those methods will create appropriate child widget. "Tcl::Tk" module will generate an unique name of newly created widget.

To demonstrate this concept:

    my $label = $frame->Label(-text => "Hello world");

executes the command

    $int->call("label", ".l", "-text", "Hello world");

and this command similar to

    $int->Eval("label .l -text {Hello world}");

This way Tcl::Tk widget commands are translated to Tcl syntax and directed to Tcl interpreter; understanding this helps in idea, why two approaches with dealing with widgets are interchangeable.

Newly created widget $label will be blessed to package "Tcl::Tk::Widget::Label" which is isa-"Tcl::Tk::Widget"

OO explanations of Widget-s of Tcl::Tk

"Tcl::Tk" widgets use object-oriented approach, which means a quite concrete object hierarchy presents. Interesting point about this object system - it is very dynamic. Initially no widgets objects and no widget classes present, but they immediately appear at the time when they needed.

So they virtually exist, but come into actual existence dynamically. This dynamic approach allows same usage of widget library without any mention from within "Tcl::Tk" module at all.

Let us look into following few lines of code:

  my $text = $mw->Text->pack;
  $text->insert('end', -text=>'text');
  $text->windowCreate('end', -window=>$text->Label(-text=>'text of label'));

Internally, following mechanics comes into play. Text method creates Text widget (known as "text" in Tcl/Tk environment). When this creation method invoked first time, a package "Tcl::Tk::Widget::Text" is created, which will be OO presentation of all further Text-s widgets. All such widgets will be blessed to that package and will be in ISA-relationship with "Tcl::Tk::Widget".

Second line calls method "insert" of $text object of type "Tcl::Tk::Widget::Text". When invoked first time, a method "insert" is created in package "Tcl::Tk::Widget::Text", with destiny to call "invoke" method of our widget in Tcl/Tk world.

At first time when "insert" is called, this method does not exist, so AUTOLOAD comes to play and creates such a method. Second time "insert" called already existing subroutine will be invoked, thus saving execution time.

As long as widgets of different type 'live' in different packages, they do not intermix, so "insert" method of "Tcl::Tk::Widget::Listbox" will mean completely different behaviour.

explanations how Widget-s of Tcl::Tk methods correspond to Tcl/Tk

Suppose $widget isa-"Tcl::Tk::Widget", its path is ".path" and method "method" invoked on it with a list of parameters, @parameters:

  $widget->method(@parameters);

In this case as a first step all @parameters will be preprocessed, during this preprocessing following actions are performed:

1.  for each variable reference its Tcl variable will be created and tied to it

2.  for each code reference its Tcl command will be created and tied to it

3.  each array reference considered as callback, and proper actions will be taken

After adaptation of @parameters Tcl/Tk interpreter will be requested to perform following operation:

if $method is all lowercase, "m/^[a-z]$/"
    ".path method parameter1 parameter2" ....

if $method contains exactly one capital letter inside name, "m/^[a-z]+[A-Z][a-z]+$/"
    ".path method submethod parameter1 parameter2" ....

if $method contains several capital letter inside name, "methodSubmethSubsubmeth"
    ".path method submeth subsubmeth parameter1 parameter2" ....

faster way of invoking methods on widgets

In case it is guaranteed that preprocessing of @parameters are not required (in case no parameters are Perl references to scalar, subroutine or array), then preprocessing step described above could be skipped.

To achieve that, prepend method name with underscore, "_". Mnemonically it means you are using some internal method that executes faster, but normally you use "public" method, which includes all preprocessing.

Example:

   # at following line faster method is incorrect, as \$var must be
   # preprocessed for Tcl/Tk:
   $button->configure(-textvariable=>\$var);

   # faster version of insert method of "Text" widget is perfectly possible
   $text->_insert('end','text to insert','tag');
   # following line does exactly same thing as previous line:
   $text->_insertEnd('text to insert','tag');

When doing many inserts to text widget, faster version will make execution faster.

  using any Tcl/Tk feature with Tcl::Tk module
Tcl::Tk module allows using any widget from Tcl/Tk widget library with either Tcl syntax (via Eval), or with regular Perl syntax.

In order to provide perlTk syntax to any Tcl/Tk widget, only single call should be made, namely 'Declare' method. This is a method of any widget in Tcl::Tk::Widget package, and also exactly the same method of Tcl::Tk interpreter object

Syntax is

 $widget->Declare('perlTk_widget_method_name','tcl/tk-widget_method_name',
    @options);

or, exactly the same,

 $interp->Declare('perlTk_widget_method_name','tcl/tk-widget_method_name',
    @options);

Options are:

  -require => 'tcl-package-name'
  -prefix => 'some-prefix'

'-require' option specifies that said widget requires a Tcl package with a name of 'tcl-package-name'; '-prefix' option used to specify a part of autogenerated widget name, usually used when Tcl widget name contain non-alphabet characters (e.g. ':') so to keep autogenerated names syntactically correct.

A typical example of such invocation is:

  $mw->Declare('BLTNoteBook','blt::tabnotebook',-require=>'BLT',-prefix=>'bltnbook');

After such a call the Tcl::Tk module will take knowledge about the tabnotebook widget from within BLT package and create proper widget creation method for it with a name BLTNoteBook. This means following statement:

 my $tab = $mw->BLTNoteBook;

will create blt::tabnotebook widget. Effectively, this is similar to following Tcl/Tk code:

  package require BLT # but invoked only once
  blt::tabnotebook .bltnbook1

Also, Perl variable $tab will contain ordinary Tcl/Tk widget that behaves in usual way, for example:

  $tab->insert('end', -text=>'text');
  $tab->tabConfigure(0, -window=>$tab->Label(-text=>'text of label'));

These two lines are Tcl/Tk equivalent of:

  .bltnbook1 insert end -text {text}
  .bltnbook1 tab configure 0 -window [label .bltnbook1.lab1 -text {text of label}]

Given all previously said, you can also write intermixing both approaches:

  $interp->Eval('package require BLT;blt::tabnotebook .bltnbook1');
  $tab = $interp->widget('.bltnbook1');
  $tab->tabConfigure(0, -window=>$tab->Label(-text=>'text of label'));

using documentation of Tcl/Tk widgets for applying within Tcl::Tk module

As a general rule, you need to consult TCL man pages to realize how to use a widget, and after that invoke perl command that creates it properly. When reading Tcl/Tk documentation about widgets, quite simple transformation is needed to apply to Tcl::Tk module.

Suppose it says:

  pathName method-name optional-parameters
     (some description)

you should understand, that widget in question has method "method-name" and you could invoke it as

  $widget->method-name(optional-parameters);

$widget is that widget with pathName, created with perl/Tk syntax, or fetched by "$int->widget" method.

Sometimes in Tcl/Tk method-name consist of two words (verb1 verb2), in this case there are two ways to invoke it, "$widget->verb1('verb2',...);" or it "$widget->verb1Verb2(...);" - those are identical.

Widget options are same within Tcl::Tk and Tcl/Tk.

"$int->widget( path, widget-type )" method

When widgets are created they are stored internally and could be retrieved by widget(), which takes widget path as first parameter, and optionally widget type (such as Button, or Text etc.). Example:

    # this will retrieve widget, and then call configure on it
    widget(".fram.butt")->configure(-text=>"new text");

    # this will retrieve widget as Button (Tcl::Tk::Widget::Button object)
    my $button = widget(".fram.butt", 'Button');

    # same but retrieved widget considered as general widget, without
    # concrete specifying its type (Tcl::Tk::Widget object)
    my $button = widget(".fram.butt");

Please note that this method will return to you a widget object even if it was not created within this module, and check will not be performed whether a widget with given path exists, despite of fact that checking for existence of a widget is an easy task (invoking "$interp->Eval("info commands $path");" will do this). Instead, you will receive perl object that will try to operate with widget that has given path even if such path do not exists. In case it do not actually exist, you will receive an error from Tcl/Tk.

To check if a widget with a given path exists use Tcl::Tk::Exists($widget) subroutine. It queries Tcl/Tk for existence of said widget.

"widget_data" method

If you need to associate any data with particular widget, you can do this with "widget_data" method of either interpreter or widget object itself. This method returns same anonymous hash and it should be used to hold any keys/values pairs.

Examples:

  $interp->widget_data('.fram1.label2')->{var} = 'value';
  $label->widget_data()->{var} = 'value';

  Non-widget Tk commands
Many non-widget Tk commands are also available within Tcl::Tk module, such as "focus", "wm", "winfo" and so on. If some of them not present directly, you can always use "$int->Eval('...')" approach.

  Miscellaneous methods
"$widget->tooltip("text")" method

Any widget accepts "tooltip" method, accepting any text as parameter, which will be used as floating help text explaining the widget. The widget itself is returned, so to provide convenient way of chaining:

  $mw->Button(-text=>"button 1")->tooltip("This is a button, m-kay")->pack;
  $mw->Entry(-textvariable=>\my $e)->tooltip("enter the text here, m-kay")->pack;

"tooltip" method uses "tooltip" package, which is a part of "tklib" within Tcl/Tk, so be sure you have it available to your Tcl/Tk.

"$int->create_rotext()" method

This method creates "rotext" type of widget within Tcl/Tk, which then could be used for example as

  $int->Eval('rotext .ro1');

"$int->create_scrolled_widget("widgetname")" method

This method creates "scrolled" type of widget for a given widget type within Tcl/Tk. For example:

  $int->create_scrolled_widget("canvas");
  $int->Eval('scrolled_canvas .scanv');

This way you can even create a perl/Tk-style widget to be initially scrollable:

  $int->create_scrolled_widget("text"); # introduce scrolled_text in Tcl/Tk
  $int->Declare('SText','scrolled_text'); # bind scrolled_text to Tcl::Tk as SText
  # now use SText instead of Scrolled('Text',...) everywhere in program
  $int->mainwindow->SText->pack(-fill=>'both');

The scrolling is taken from snit (scrodgets), and the resulting widget have both scrolled options/methods and widget's options/methods.

Points of special care

list context and scalar context
    When widget method returns some result, this result becomes transformed according to the context, either list or scalar context. Sometimes this transformation is right, but sometimes its not. Unfortunately there are many cases, when Tcl/Tk returns a string, and this string become broken into words, because the function call is placed in list context.

    In such cases concatenate such call with empty string to force right behaviour:

      use Tcl::Tk;
      my $mw = Tcl::Tk::tkinit;
      my $int = $mw->interp;
      my $but = $mw->Button(-text=>'1 2  3')->pack;
      print "[", $but->cget('-text'), "] wrong - widget method returns 3 values!\n";
      print "[", "".$but->cget('-text'), "] CORRECT - 1 value in scalar context\n";
      $int->MainLoop;

    Actually, the example above will work correctly, because currently list of function names having list results are maintained. But please contact developers if you find misbehaving widget method!

AUTHORS

Malcolm Beattie
Vadim Konovalov
Jeff Hobbs
Gisle Aas

COPYRIGHT
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.